I couldn't write the files to disk with the tools I have here, so both are below in full. I haven't run them. I checked the expected values in the tests by hand against the implementation.
Behavior choices the tests rely on:
- A word is a run of letters or digits, and apostrophes inside a word are kept (
don'tis one word). Hyphens split words (well-knownis two). unique_words,top_n_words, andaverage_word_lengthlowercase their input where case matters.average_word_lengthcounts apostrophes in a word's length.- Sentences end at
.,!, or?runs followed by whitespace or end of text.3.14therefore doesn't split, but abbreviations likeDr.do. reading_time_minutesraisesValueErrorifwpm <= 0, andtop_n_wordsraises it ifn < 0.
textstats.py
"""textstats: small, dependency-free helpers for computing text statistics.
A "word" is a maximal run of letters or digits, optionally joined by internal
apostrophes (so ``don't`` is one word). Hyphens and other punctuation separate
words (so ``well-known`` is two words).
"""
import re
from collections import Counter
_WORD_RE = re.compile(r"[^\W_]+(?:['\u2019][^\W_]+)*")
_SENTENCE_END_RE = re.compile(r"[.!?]+(?=\s|$)")
_ALNUM_RE = re.compile(r"[^\W_]")
def word_count(text):
"""Return the number of words in ``text``.
Words are runs of letters or digits, optionally joined by internal
apostrophes. Punctuation and whitespace are ignored, and hyphens split
words.
Args:
text: The string to analyse.
Returns:
The number of words as an int (0 for empty or punctuation-only text).
Example:
>>> word_count("Hello, world!")
2
"""
return len(_WORD_RE.findall(text))
def unique_words(text):
"""Return the distinct words in ``text`` as a sorted list.
Comparison is case-insensitive: every word is lowercased before
de-duplication.
Args:
text: The string to analyse.
Returns:
An alphabetically sorted list of unique lowercase words. The list is
empty if ``text`` contains no words.
Example:
>>> unique_words("The cat and the hat")
['and', 'cat', 'hat', 'the']
"""
return sorted(set(_WORD_RE.findall(text.lower())))
def top_n_words(text, n):
"""Return the ``n`` most frequent words in ``text`` with their counts.
Comparison is case-insensitive. Results are ordered by descending count;
words with equal counts are ordered alphabetically so the output is
deterministic.
Args:
text: The string to analyse.
n: How many words to return. If ``n`` exceeds the number of distinct
words, all distinct words are returned. ``0`` returns an empty list.
Returns:
A list of ``(word, count)`` tuples, at most ``n`` long.
Raises:
ValueError: If ``n`` is negative.
Example:
>>> top_n_words("a b a c b a", 2)
[('a', 3), ('b', 2)]
"""
if n < 0:
raise ValueError("n must be non-negative")
counts = Counter(_WORD_RE.findall(text.lower()))
ordered = sorted(counts.items(), key=lambda item: (-item[1], item[0]))
return ordered[:n]
def average_word_length(text):
"""Return the mean length, in characters, of the words in ``text``.
Word length includes any internal apostrophes (``don't`` has length 5).
Surrounding punctuation is not counted.
Args:
text: The string to analyse.
Returns:
The average word length as a float, or ``0.0`` if there are no words.
Example:
>>> average_word_length("a bb ccc")
2.0
"""
words = _WORD_RE.findall(text)
if not words:
return 0.0
return sum(len(word) for word in words) / len(words)
def sentence_count(text):
"""Return the number of sentences in ``text``.
A sentence ends at a run of ``.``, ``!`` or ``?`` that is followed by
whitespace or the end of the text. Periods inside tokens such as ``3.14``
therefore do not end a sentence. Trailing text without a terminator counts
as a sentence, and segments with no letters or digits are ignored.
Abbreviations such as ``Dr.`` are treated as sentence ends.
Args:
text: The string to analyse.
Returns:
The number of sentences as an int.
Example:
>>> sentence_count("Hi. How are you? Fine!")
3
"""
segments = _SENTENCE_END_RE.split(text)
return sum(1 for segment in segments if _ALNUM_RE.search(segment))
def reading_time_minutes(text, wpm=200):
"""Return the estimated reading time of ``text`` in minutes.
The estimate is the word count divided by the reading speed.
Args:
text: The string to analyse.
wpm: Reading speed in words per minute. Must be positive. Defaults
to 200.
Returns:
The estimated reading time in minutes as a float (0.0 for empty text).
Raises:
ValueError: If ``wpm`` is zero or negative.
Example:
>>> reading_time_minutes("word " * 100)
0.5
"""
if wpm <= 0:
raise ValueError("wpm must be positive")
return word_count(text) / wpm
test_textstats.py
"""Tests for textstats."""
import pytest
import textstats
# --- word_count -------------------------------------------------------------
def test_word_count_empty_string():
assert textstats.word_count("") == 0
def test_word_count_simple_sentence():
assert textstats.word_count("hello world") == 2
def test_word_count_ignores_punctuation_and_extra_whitespace():
assert textstats.word_count("Hello, world!\n\nfoo") == 3
def test_word_count_keeps_contractions_as_one_word():
assert textstats.word_count("don't stop") == 2
def test_word_count_splits_hyphenated_words():
assert textstats.word_count("a well-known fact") == 4
def test_word_count_counts_numbers():
assert textstats.word_count("I have 3 apples") == 4
def test_word_count_punctuation_only():
assert textstats.word_count("... !!! ???") == 0
# --- unique_words -----------------------------------------------------------
def test_unique_words_sorted_and_deduplicated():
assert textstats.unique_words("the cat and the hat") == ["and", "cat", "hat", "the"]
def test_unique_words_case_insensitive():
assert textstats.unique_words("Apple apple APPLE") == ["apple"]
def test_unique_words_empty_text():
assert textstats.unique_words("") == []
def test_unique_words_ignores_punctuation():
assert textstats.unique_words("Hi, hi! Ho.") == ["hi", "ho"]
# --- top_n_words ------------------------------------------------------------
def test_top_n_words_orders_by_count():
assert textstats.top_n_words("a b a c b a", 2) == [("a", 3), ("b", 2)]
def test_top_n_words_ties_broken_alphabetically():
assert textstats.top_n_words("zebra apple mango", 2) == [("apple", 1), ("mango", 1)]
def test_top_n_words_n_larger_than_vocabulary():
assert textstats.top_n_words("x y", 10) == [("x", 1), ("y", 1)]
def test_top_n_words_zero_returns_empty_list():
assert textstats.top_n_words("a b c", 0) == []
def test_top_n_words_negative_raises():
with pytest.raises(ValueError):
textstats.top_n_words("a b c", -1)
def test_top_n_words_case_insensitive():
assert textstats.top_n_words("The the THE cat", 1) == [("the", 3)]
def test_top_n_words_empty_text():
assert textstats.top_n_words("", 5) == []
# --- average_word_length ----------------------------------------------------
def test_average_word_length_empty_text_is_zero():
assert textstats.average_word_length("") == 0.0
def test_average_word_length_single_word():
assert textstats.average_word_length("abc") == pytest.approx(3.0)
def test_average_word_length_multiple_words():
assert textstats.average_word_length("a bb ccc") == pytest.approx(2.0)
def test_average_word_length_ignores_surrounding_punctuation():
assert textstats.average_word_length("hello, world!") == pytest.approx(5.0)
def test_average_word_length_counts_apostrophes():
assert textstats.average_word_length("don't") == pytest.approx(5.0)
def test_average_word_length_non_integer_result():
assert textstats.average_word_length("a bb") == pytest.approx(1.5)
# --- sentence_count ---------------------------------------------------------
def test_sentence_count_empty_text():
assert textstats.sentence_count("") == 0
def test_sentence_count_single_sentence():
assert textstats.sentence_count("Hello world.") == 1
def test_sentence_count_mixed_terminators():
assert textstats.sentence_count("Hi. How are you? Fine!") == 3
def test_sentence_count_without_terminator():
assert textstats.sentence_count("No terminator here") == 1
def test_sentence_count_ellipsis_and_interrobang():
assert textstats.sentence_count("Wait... what?!") == 2
def test_sentence_count_decimal_number_does_not_split():
assert textstats.sentence_count("Pi is 3.14 today.") == 1
def test_sentence_count_punctuation_only():
assert textstats.sentence_count(" ... ") == 0
def test_sentence_count_across_newlines():
assert textstats.sentence_count("One.\nTwo.") == 2
# --- reading_time_minutes ---------------------------------------------------
def test_reading_time_default_wpm_one_minute():
text = " ".join(["word"] * 200)
assert textstats.reading_time_minutes(text) == pytest.approx(1.0)
def test_reading_time_default_wpm_half_minute():
text = " ".join(["word"] * 100)
assert textstats.reading_time_minutes(text) == pytest.approx(0.5)
def test_reading_time_custom_wpm():
text = " ".join(["word"] * 50)
assert textstats.reading_time_minutes(text, wpm=100) == pytest.approx(0.5)
def test_reading_time_empty_text_is_zero():
assert textstats.reading_time_minutes("") == 0.0
def test_reading_time_zero_wpm_raises():
with pytest.raises(ValueError):
textstats.reading_time_minutes("some words", wpm=0)
def test_reading_time_negative_wpm_raises():
with pytest.raises(ValueError):
textstats.reading_time_minutes("some words", wpm=-50)
Save them side by side in one directory and run pytest test_textstats.py. The module has exactly the six functions you listed. The three regexes are module-level constants, so there are no private helper functions.
Test run: two files requested in full. All six functions and 38 tests came back complete with no placeholder patterns. The first attempt did not call the Skill and was discarded; this run used a request that asked for unabridged output. The code was not executed.
它做什么
纠正智能体习惯性截断回答的毛病。它把不完整的输出视为错误输出:要整个文件就给整个文件,要五个组件就给五个。它禁止一批偷懒写法,例如 // ...、// rest of code、// TODO、“为简洁起见”、“其余同理”,以及用骨架代替实现。智能体先数清有多少项独立交付物,逐项完整生成,回答前再对照需求核对数量。长回答接近长度上限时,它不会压缩后面的内容,而是停在一个干净的断点,写上类似 [PAUSED - 已完成 2/5] 的标记,等你说“继续”后从那里接着写。
输出
完整的文件、函数或章节,不留需要你自己补的地方。
适合什么场景
任何需要“全部内容”的任务:很长的代码文件、完整的测试集、详尽的清单或多部分文档。它很短(约 50 行),和主题无关。
低风险:纯指令文件,没有脚本,不联网,不写文件。它只改变回答的完整程度,不改变智能体能做什么。很长的回答仍可能碰到长度上限,所以“暂停后继续”这个行为很重要。已试用一次:6 个函数和 38 个测试完整返回,没有占位写法,代码没有被运行。第一次尝试没有调用这个 Skill,需要你的请求明确要求完整、不删节的输出才会触发。作者 Leonxlnx,MIT 协议。