Release v0.1.0: production hardening#7
Conversation
Introduce errors.py with an AgentlingError base and domain subclasses, and reparent ToolCallError under it so one except catches everything the framework raises. Replace the placeholder __init__.py with real re-exports and __all__, and read the version from installed package metadata so it cannot drift from pyproject.
Move mutable run state (memory, the interrupt token, and dynamically loaded skill tools) off Agent onto a new AgentSession, so one Agent can be built once and shared safely across concurrent runs. Agent becomes config plus a factory: agent.start() mints a session, and agent.run() is a one-shot convenience that runs on a fresh session. Each session copies the base tool set, so a skill loaded in one run never leaks into another. Breaking: agent.memory -> session.memory, agent.interrupt() -> session.interrupt(). Migrate the affected tests and add session-isolation tests.
Route both the streaming and non-streaming paths through one parse_tool_arguments that raises ModelOutputError on invalid JSON or a non-object arguments payload, and reject a streamed tool call with no name. The loop catches ModelOutputError, re-prompts the model with a correction up to a small cap, then raises ModelError if it keeps failing. Previously a bad JSON tool-call argument raised JSONDecodeError straight out of the loop and killed the run.
Wrap tool.call in asyncio.wait_for (a per-tool Tool.timeout overrides the agent's tool_timeout); a timed-out tool becomes a ToolTimeoutError observation the model can recover from, while a genuine task cancellation propagates and stops the run. Bound each model turn with asyncio.timeout (model_timeout) and check the interrupt token between streamed chunks, so a long or hung stream can be stopped without waiting for it to finish.
Run sync @tool functions via asyncio.to_thread so a blocking call cannot stall the event loop. Add per-tool metadata (timeout, parallel_safe, max_output_chars) through the @tool decorator, which now works bare or parameterized. Truncate long tool observations head and tail (max_tool_output_chars). Redact unexpected tool exceptions to the type only, logging the detail, when redact_errors is set, while ToolCallError stays fully visible so the model can fix its arguments. Validate tool names against provider naming constraints.
… wording Validate the version and shape in Memory.from_dict, raising MemoryLoadError instead of a raw KeyError. Accumulate token usage across turns and report the run total on the terminal FinalEvent (Usage gains __add__). Add an optional context_manager hook on Agent to trim or summarize the prompt before each model call. Tag tool failures with an error_kind so an observation says "fix the arguments" only for a validation error and "try a different approach" otherwise.
FinalEvent gains a status field (completed / interrupted / max_steps) so a stream consumer can tell how a run ended; failures still raise. Interrupt keeps writing no FinalStep, so the resume contract holds and the distinction lives in the event. print_events takes a file argument for testing and redirection, and the module documents the event ordering guarantees.
Declare ruff and mypy configuration in pyproject and pin them as dev dependencies so CI runs the same versions instead of fetching them ad hoc. CI now also checks formatting and builds the package. Reformat the tree with ruff format. Bump the version to 0.1.0.
Extend the README config reference with the timeout, redaction, truncation, and context-manager knobs, refresh the dev commands, and add a security note for skill-provided tools. Add SECURITY.md (trust model and reporting), CONTRIBUTING.md, and CHANGELOG.md.
PR Summary by QodoRelease v0.1.0: production hardening (sessions, robustness, packaging)
AI Description
Diagram
High-Level Assessment
Files changed (19)
|
Declare ruff and mypy configuration in pyproject and pin them as dev dependencies so CI runs the same versions instead of fetching them ad hoc. CI now also checks formatting and builds the package. Reformat the tree with ruff format. Bump the version to 0.1.0.
Code Review by Qodo
1. load_skill tool shadowing
|
agglomerate_deltas now rejects a streamed tool call with no id, as it already did for a missing name, since an empty id breaks the tool-result round-trip. Skill loading validates that `tools:` is a list of strings and requires exact `---` fence lines, so a stray string or a leading `---` in the body is not mistaken for frontmatter.
Extend the README config reference with the timeout, redaction, truncation, and context-manager knobs, note the sync-tool timeout caveat, refresh the dev commands, and add a security note for skill-provided tools. Add SECURITY.md, CONTRIBUTING.md, and CHANGELOG.md.
agglomerate_deltas now rejects a streamed tool call with no id, as it already did for a missing name, since an empty id breaks the tool-result round-trip. Skill loading validates that `tools:` is a list of strings and requires exact `---` fence lines, so a stray string or a leading `---` in the body is not mistaken for frontmatter.
Correct the ModelOutputError docstring (now also a missing id), the event ordering docstring (all tool-call events precede all tool-result events), the malformed-call comment, and a test section label. Update the README with a value-first opener, an alpha status note, real CI and PyPI badges, an errors.py module row, the two-hint self-healing wording, a sync-tool timeout caveat, the production knobs, and a Limitations section. Add SECURITY.md, CONTRIBUTING.md, and CHANGELOG.md.
Reserve the load_skill tool name when skills are configured, so a caller tool cannot silently shadow the built-in skill loader (raise at construction). Fix _truncate_middle to cut the head for tiny limits (0 or 1), where keep=0 and text[-0:] would otherwise return the whole string.
Six self-contained examples that progress from a basic tool agent to failure recovery, memory replay, and a full event observer. The three offline examples (failure_recovery, memory_chat, advanced_observer) run with a scripted model and no API key; the API-backed three (math_tutor, repo_assistant, notes_agent) take an injectable model so tests drive them offline too. Adds tests/test_examples.py (no network), a README examples section with a feature matrix, a pytest pythonpath so examples import, and extends CI's ruff/mypy to cover examples.
Path A hardening (FOL-47) plus packaging for the first real release.
133 tests; ruff + mypy clean;
uv buildproduces agentling-0.1.0.Closes FOL-44.