Skip to content

v0.6.0 — Streaming tool calls

Latest

Choose a tag to compare

@Ferdea Ferdea released this 11 Sep 04:43

ember 0.6.0

Tool calling now works in streaming mode, and agent memory got a strict, predictable session contract.

Highlights

Tool calling in streaming mode (#45)

Agent.stream_run() no longer refuses to run with tools — it executes tool calls in a loop exactly like run(): results go back to the model as tool messages, and generation continues until the final text answer.

for fragment in agent.stream_run("What's the weather in Moscow?"):             
    print(fragment, end="", flush=True)                                        

• StreamChunk gained a tool_calls field. The adapter emits it exactly once per round — in the final chunk, fully reassembled from the stream deltas, with delta empty; it is None in every other chunk.
• OpenAIProvider.stream() reassembles tool-call deltas, including parallel calls, which the OpenAI SDK splits across chunks by index and by field (id / name / arguments).
• ToolCallLimitError semantics now match run(): the shared _tool_call_limit_error() helper guards both paths, so max_tool_steps (default 10) applies identically.

Two things worth knowing:

• The stream is not segmented into rounds: interim replies before tool calls and the final answer arrive back-to-back in a single stream, and you cannot tell them apart. Use the non-streaming run() if you need that.
• While a tool runs (a shell command, an MCP server call) the stream is simply silent — Iterator[str] has no way to signal "work in progress".

Stricter session IDs in memory (#46)

Session IDs used to be silently sanitized: every character outside [A-Za-z0-9_.-] was replaced with _. That was not reversible — "ручная" and "ручной" both collapsed into the file ______.json, the second save_session() overwrote the first, and load_session() returned someone else's data with no indication of error. The same collision broke exclude_session_id in search(): the current session was not excluded from recall.

IDs are now validated instead of rewritten. Allowed: Latin letters, digits, _, -, and . (not as the first or last character), up to 128 characters. Anything else raises InvalidSessionIdError — a subclass of ValueError, so existing except ValueError handlers keep working.

The ASCII restriction is deliberate: on APFS/HFS+ filenames are normalized, and Unicode IDs would reintroduce unresolvable collisions. Since a valid ID equals the filename without its extension, the ID is recovered unambiguously from the directory.

▌ Migration: if you pass non-ASCII or otherwise unusual IDs today, they were
▌ silently mangled before. Rename them to valid IDs — or, if you rely on the
▌ old behavior, sanitize them yourself before constructing the agent.

Enumerate sessions: Memory.list_sessions() (#46)

Consumers (CLIs, UIs) no longer need to read *.json files directly just to show a session list.

 from ember import FileMemory                                                   
                                                                                
 memory = FileMemory("/tmp/ember-memory")                                       
 for info in memory.list_sessions():                                            
     print(info.session_id, info.message_count, info.updated_at)                
 # user-42 2 2026-09-11 04:25:58+00:00                                          

• New SessionInfo dataclass (frozen, slots): session_id, message_count, updated_at (UTC), and size_bytes (None when the backend doesn't track size).
• list_sessions() is an abstractmethod like the rest of Memory — custom backends (Redis, Postgres, SQLite) must implement it.
• Ordering is part of the contract: newest first, ties broken by ascending session_id, so the output can be rendered as-is.
• FileMemory counts non-empty JSONL lines without parsing content (one corrupt file doesn't break listing the others) and filters stray *.json files by the same rule as search().

Installation

 pip install "emberio-labs-ember[openai]"   # with OpenAI support               
 pip install "emberio-labs-ember[mcp]"      # with MCP client support           
 pip install emberio-labs-ember             # core (no providers)               

On PyPI the package is published as emberio-labs-ember; the import name is still
ember.

Compatibility

No public API removals. Behavior changes to be aware of:

• stream_run() with tools set used to raise ValueError; it now runs the tool loop.
• Invalid session_id values that used to be silently sanitized now raise InvalidSessionIdError (a ValueError subclass).