aimu.context¶
Plain functions over a conversation's list[dict]: estimate its size, trim it to a token
budget, or replace the older part with an LLM-generated summary. No ContextPolicy class, no
hidden rewriting inside a client. See how-to: manage context.
aimu.context.count_tokens ¶
Estimate the token count of messages.
The default counter is len(text) // 4, a rule-of-thumb estimate -- it is not a
real tokenizer and will be wrong (usually within 20-30%) for any specific model. Exact
counts exist only after the fact, via client.last_usage on a real request. Pass
counter= (any Callable[[str], int], e.g. a real tokenizer's encode+len)
for accuracy.
text is each message JSON-serialized (structural overhead included, since it rides
along in the real request) with AIMU's own inert bookkeeping keys (timestamp,
thinking, provenance) stripped first, since a provider never sees those. See the
module docstring for the full rationale.
aimu.context.trim_messages ¶
trim_messages(messages: list[dict], max_tokens: int, *, keep_system: bool = True, keep_last: int = 2, counter: Optional[Callable[[str], int]] = None) -> list[dict]
Return a new, trimmed message list that fits within max_tokens (estimated).
Always returns a new list; messages (and its dicts) are never mutated. A conversation already at or under max_tokens is returned unchanged (a copy) -- this is a no-op, not a forced rewrite.
Dropping proceeds oldest-first over the non-system messages, one indivisible group at a
time (see :func:_group_messages): an assistant message carrying tool_calls is
always dropped together with every tool message answering it, never separately. This
is the invariant that justifies this function over a hand-written messages[-n:]
slice, which produces exactly that illegal shape.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
messages
|
list[dict]
|
The conversation, OpenAI message-dict format. |
required |
max_tokens
|
int
|
The token budget to trim down to, per |
required |
keep_system
|
bool
|
When |
True
|
keep_last
|
int
|
The number of trailing messages (not exchanges -- see the module docstring) to always protect from dropping, extended outward to whole tool-call groups when the raw count would otherwise split one. |
2
|
counter
|
Optional[Callable[[str], int]]
|
Optional real tokenizer; see :func: |
None
|
aimu.context.summarize_messages ¶
summarize_messages(client: Any, messages: list[dict], *, keep_last: int = 2, prompt: Optional[str] = None) -> list[dict]
Replace the older part of a conversation with a one-message LLM summary.
Any {"role": "system"} messages are always preserved unchanged, and are returned at
the front (a mid-conversation system message is hoisted, the same reordering
:func:trim_messages does under keep_system=True). Of the rest, the
last keep_last messages (see the module docstring on why messages, not
exchanges; extended outward to whole tool-call groups per :func:_group_messages, the
same invariant :func:trim_messages enforces) are kept verbatim as the tail. Everything
older than that (the "prefix") is rendered to a plain transcript, summarized by one
client.generate() call, and replaced with a single {"role": "system", ...}
message carrying the summary text -- system, rather than user or assistant, because it
is out-of-band context neither party actually said, not a real conversational turn.
client is any object exposing generate(prompt: str) -> str (a plain
:class:~aimu.models.BaseModelClient, or a :class:~aimu.agents.Agent via
agent.as_model_client()); this function takes it as an argument rather than
constructing one itself, so it stays free of any provider dependency.
When there is nothing older than the protected tail (the whole conversation already
fits in keep_last), no summarization call is made and the conversation is returned
unchanged (a new list; messages is never mutated).
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
client
|
Any
|
Anything with a |
required |
messages
|
list[dict]
|
The conversation, OpenAI message-dict format. |
required |
keep_last
|
int
|
Trailing messages (not exchanges) to keep verbatim; see above. |
2
|
prompt
|
Optional[str]
|
Optional instruction prepended to the rendered transcript, replacing
:data: |
None
|
aimu.context.DEFAULT_SUMMARIZE_PROMPT
module-attribute
¶
DEFAULT_SUMMARIZE_PROMPT = 'Summarize the conversation below in a few sentences. Preserve concrete facts, decisions, and any open questions a continuation of this conversation would still need to know.'