Repository navigation
v1.3.4
A patch release with fixes found while running the promptise.com guides. Upgrade with pip install -U promptise.
Highlights: max_agent_iterations=N now allows exactly N model calls; budget and hook stops raise GraphBudgetExceededError / GraphStoppedError instead of returning a raw tool result; invalid MCP tool calls over HTTP now go through auth, rate limiting and audit with a bounded error; credential redaction keeps JSON valid; context compaction no longer pushes models into re-read loops; the "Available tools" prompt block is opt-in (tool_summary). Read Changed before upgrading.
Fixed
max_agent_iterations=Nran N+1 model calls -- the engine counted a node execution after running it and stopped only once the count passed the limit, sobuild_agent(max_agent_iterations=N)(the engine'smax_iterations) allowed N+1 node executions: N+1 model calls and N+1 rounds of tool calls in the default ReAct graph.Nnow allows exactlyNnode executions, which in the ReAct graph isNmodel calls. Thebuild_agentdocstring and the engine docs say what is counted.BudgetHookstopped a run silently and returned a raw tool result as the answer -- when the budget ran out in the middle of a tool loop,ainvoke()returned normally with the last tool result as the last message andlast_report.errorunset. Such a run now raisesGraphBudgetExceededError(a newGraphStoppedError, itself aGraphExecutionError) withreason("Token budget exceeded: 5078/5000 tokens"),stopped_by,pending_toolsand the report, likeGraphIterationLimitErrorfor iteration limits. Any hook that ends a run the same way (CycleDetectionHook, a custom hook) raisesGraphStoppedError. A budget reached on the node that answered still returns the answer.- One
BudgetHookcapped an engine's lifetime, not a run --tokens_usedwas never reset, so after the first run hit the cap every later run on the same engine stopped after its first node. The hook now counts the run's own tokens (GraphState.total_tokens), so every run, concurrent ones included, gets the whole budget;tokens_used/cost_usedreport the latest run. - Context compaction could make a model re-read the same results until its budget ran out -- a result longer than
keep_result_charswas cut in the ledger with a note telling the model to call the tool again for the full text, and the re-read was cut again as soon as the next exchange started, so a model that re-read cut results one at a time could never have them all in full (reproduced with a scripted model: the default ReAct agent looped to its iteration limit). A call the model made more than once is now shown in full in the ledger (and shrunk last undermax_tokens), and the excerpt says how much was cut and that a re-read is kept in full. - Schema-invalid tool calls skipped the server pipeline over HTTP and stdio, and echoed the whole input -- the MCP SDK (1.10+) validated a call's arguments against the tool's
inputSchemabefore Promptise saw the call, so an invalid call was never authenticated, rate limited or audited, and came back as plain text,Input validation error: <jsonschema message>, quoting the offending value in full (a 1 MB string againstmax_length=10produced a 1 MB error). The SDK's check is now off; the server validates every call itself, inside its pipeline, on every transport and inTestClient, and the error is the structuredVALIDATION_ERRORenvelope. VALIDATION_ERRORenvelopes could grow with the input -- one entry per invalid item, so a list of 50,000 bad values produced a 9 MB error. The envelope now lists at most 20 field errors (details.omitted_errorscounts the rest), clips field paths and messages, and caps the message at 1,000 characters; it never includes the rejected value.- Redacting credentials corrupted JSON -- the connection-string patterns (
postgres://,mysql://,mongodb://,redis://) and thepassword=/secret=patterns ran to the next whitespace, so on a JSON tool result they swallowed the closing quote, brace and comma:{"config": {"db": "postgres://..."}, "region": "eu"}became{"config": {"db": "[POSTGRES_CONNECTION] "region": "eu"}. Those patterns now stop at quotes and backslashes (URLs also at<>{}), and when the scanned text is a JSON document a redaction replaces text inside a string value only: the result parses to the same structure, JSON encoded inside a string stays valid, escape sequences are never split, and a number that has to be redacted becomes a quoted label. - mcp 2.x clients logged a 31-error WARNING on every connection to a Promptise server -- an mcp 2.x client (current Pydantic AI, OpenAI Agents SDK) opens with
server/discover, which the 1.x SDK failed to validate, loggingFailed to validate request: 31 validation errors for ClientRequest(94 lines) before answering-32602. The server now answers a request for any method it does not implement with-32601 Method not foundand logs it at DEBUG; the client falls back toinitializeas before. A notification of an unknown method is ignored. Works with every supportedmcpversion (>=1.9,<2). - The Quick Start's first agent raised
TypeError-- "Your First Agent" callsbuild_agent(model=..., instructions=...), butserverswas a required keyword, so the first example a new user ran failed withbuild_agent() missing 1 required keyword-only argument: 'servers'.serversis now optional (see Changed). - The Quick Start's "Add Tools" example never started its tool server -- the agent launched
tools.pywithout--serve, so the child process ran the agent again (which launched anothertools.py, and so on) instead of serving its tools, and the MCP handshake never happened. The example now passesargs=["tools.py", "--serve"]. The same example in the blog posts "What is MCP", "Build an AI agent with Promptise" and "How to build an AI agent in Python" is fixed too (the last one also never calledserver.run()). Everypythonblock on the Quick Start page now runs in the test suite with a scripted model, the stdio example against its real server. - Health: one anomaly silenced every other anomaly of its kind for 5 minutes --
HealthConfig.cooldown(300 s by default) was kept per anomaly type, so once the agent was reported stuck on one call, getting stuck on a different call (in the same run or in the next runs) was not reported: no anomaly, nohealth.anomalyevent, noon_anomalyaction. The cooldown now applies to the same anomaly only: the same stuck call (tool and arguments), the same loop (whichever call of the cycle it is caught at), or the same empty-response / error-rate signal. The health docs now explain the cooldown and how to useHealthMonitoras an in-run guard in apre_toolhook (cooldown=0, so every repeat is reported). - The mission judge printed "No tools discovered" on every evaluation --
build_agent()printed[promptise] No tools discovered from MCP servers; agent will run without tools.to stdout whenever an agent had no tools, and the mission judge is built without tools, so a process with a mission wrote that line into the application's output once per evaluation. See Changed. promptise audit verify --strictsummary grammar -- with one warning it read "1 warning fail the check"; it now reads "1 warning fails the check under --strict".- Open mode: the model was not told that a tool it creates is usable only from its next run -- the run that calls
create_toolkeeps the agent and tool list it started with, butcreate_toolansweredTool 'x' created. Rebuild successfuland its description said nothing about when the tool appears, so models kept looking for the tool and creating it again. See Changed. - Docs:
sandbox_custom_tools=Falsedoes not give agent-written code full Python builtins -- docs/runtime/meta-tools.md said it did. The code runs with the same restricted builtins (noopen,exec,eval) and may additionally import a fixed standard-library allowlist (json,math,re,datetime, ...); the docs and the_build_dynamic_tooldocstring now say so.
Changed
- The "Available tools" block is no longer added to the system prompt by default -- every
PromptNodewith tools appended a summary of them (parameters and the first 120 characters of each description) to its system prompt, on top of the tool definitions the provider already receives. On the 40-tool test server that is 997 tokens on every model call (a third on top of the 3,043 tokens of tool definitions); a 48-tool guide run measured 1,882 of a 6,843-token first request. Newtool_summaryoption onbuild_agent,PromptGraphEngineandPromptNode:"off"(the default),"names"(one line of names, 178 tokens on the same server) or"full"(the previous block). Settool_summary="full"to keep the 1.3.3 prompt. A string default now renders quoted (cursor: string="", wascursor: string=). - The compaction ledger no longer invites re-reading "something that changes over time" -- its header now tells the model to use the listed facts and call again only for the rest of a cut result or when it is waiting for something to change, such as a job's status. The
managedpattern's instructions say the same. Repeated calls still run the tool, so polling works as before. max_agent_iterations/PromptGraphEngine(max_iterations=...)allow one node execution fewer than in 1.3.3 (see Fixed): an agent that needed exactly N+1 steps under a limit of N now raisesGraphIterationLimitError; raise the limit by one to keep the old allowance.- A
BudgetHook(or other hook) that stops a run mid tool loop raises instead of returning (see Fixed). CatchGraphBudgetExceededError/GraphStoppedError. - Input validation runs inside the middleware chain -- a call whose arguments fail validation now goes through the server's middleware (authentication, rate limiting, audit, in your order) and the tool's guards before it gets its
VALIDATION_ERROR, on the live transports and inTestClient. An unauthenticated caller now getsAUTHENTICATION_ERRORrather than a schema error, invalid calls count against rate limits, andAuditMiddlewarerecords them (status: "error", noargs). Middleware seesctx.state["_tool_arguments"] == {}for such a call; the handler, dependency injection,CacheMiddlewareandApprovalGateMiddlewarenever see the rejected input (no cache lookup, no approval request). Resource templates and prompts follow the same order. build_agent(servers=...)is optional -- it defaults toNone, the same asservers={}: an agent with no MCP tools (just the model, orextra_tools).build_agent()no longer prints to stdout when an agent has no tools -- with no servers configured nothing is reported (aDEBUGlog record onpromptise.agent); when servers are configured but none of them offers a tool, aWARNINGnaming the servers is logged onpromptise.agentinstead of the oldprint.- Health cooldown is per anomaly, not per anomaly type -- see Fixed. A process whose agent gets stuck on several different calls now reports each (one
health.anomalyevent each, and theon_anomalyaction for the run), where it reported only the first withincooldown. Repeats of the same anomaly are still reported once percooldown, also across runs. - Open mode:
create_toolandconnect_mcp_servertell the model the change applies from its next run --create_toolnow answersTool 'x' created. You can call it from your next run, not in this one: this run keeps the tools it started with, so finish the current task without it.(it wasTool 'x' created. Rebuild successful), andconnect_mcp_serveranswersConnected to MCP server 'x' at <url>. Its tools are available from your next run, not in this one: ...(it ended withRebuild successful). Both tool descriptions say the same, andcreate_tool's description no longer saysrun(**kwargs) -> str(declared parameters are passed torunas keyword arguments).
Documentation
docs/core/engine-prebuilts.md,reasoning-patterns.md,engine-nodes.md: the ledger "cuts redundant tool calls at equal accuracy" claim is now conditional on the model, with the guide's measurements (gpt-4.1-mini re-read records under 1.3.3 compaction; gpt-5-mini did not).docs/core/tool-optimization.md: new "The tool summary in the system prompt" section with token measurements.docs/core/engine-hooks.md:BudgetHookis per run, may overshoot by one node, and raisesGraphBudgetExceededErrormid tool loop.
Full changelog: https://github.com/promptise-com/Foundry/blob/main/CHANGELOG.md