pi-python-core 0.9.0__tar.gz → 0.10.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/PKG-INFO +11 -2
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/README.md +7 -1
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/README.zh-CN.md +7 -1
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/API.md +87 -0
- pi_python_core-0.10.0/docs/CODING_AGENTS.md +176 -0
- pi_python_core-0.10.0/docs/MCP_INTERACTION.md +457 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/PLUGINS.md +112 -0
- pi_python_core-0.10.0/docs/PLUGIN_DEVELOPMENT.md +287 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/PROVIDERS.md +11 -2
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/zh/API.md +66 -0
- pi_python_core-0.10.0/docs/zh/CODING_AGENTS.md +140 -0
- pi_python_core-0.10.0/docs/zh/MCP_HOST_VERIFICATION.md +130 -0
- pi_python_core-0.10.0/docs/zh/MCP_INTERACTION.md +396 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/zh/PLUGINS.md +98 -0
- pi_python_core-0.10.0/docs/zh/PLUGIN_DEVELOPMENT.md +244 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/zh/PROVIDERS.md +11 -1
- pi_python_core-0.10.0/examples/mcp_interactive.py +160 -0
- pi_python_core-0.10.0/examples/mcp_interactive_server.py +73 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/subagent.py +2 -7
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/pyproject.toml +3 -1
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/__init__.py +7 -1
- pi_python_core-0.10.0/src/pi_python/_mcp_host.py +221 -0
- pi_python_core-0.10.0/src/pi_python/_mcp_interaction.py +1000 -0
- pi_python_core-0.10.0/src/pi_python/_mcp_process.py +183 -0
- pi_python_core-0.10.0/src/pi_python/_mcp_sampling.py +280 -0
- pi_python_core-0.10.0/src/pi_python/_version.py +1 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/agent.py +19 -0
- pi_python_core-0.10.0/src/pi_python/features.py +45 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/function_tools.py +134 -99
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/loop.py +14 -11
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/mcp.py +136 -14
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/plugins/__init__.py +354 -28
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/plugins/_subagents.py +5 -15
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/providers/anthropic.py +3 -1
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/providers/completions.py +34 -9
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/providers/oauth.py +3 -10
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/providers/openai.py +3 -2
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/providers/transport.py +228 -126
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/run.py +28 -2
- pi_python_core-0.10.0/src/pi_python/sync.py +150 -0
- pi_python_core-0.10.0/src/pi_python/tasks.py +134 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/tools.py +58 -2
- pi_python_core-0.10.0/tests/servers/mcp_interactive_server.py +200 -0
- pi_python_core-0.10.0/tests/servers/mcp_process_server.py +86 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/servers/mcp_server.py +16 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_completions.py +74 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_contract_v3.py +50 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_examples.py +12 -1
- pi_python_core-0.10.0/tests/test_features.py +119 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_function_tools.py +161 -0
- pi_python_core-0.10.0/tests/test_lifecycle_contracts.py +278 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_mcp.py +69 -3
- pi_python_core-0.10.0/tests/test_mcp_delta.py +477 -0
- pi_python_core-0.10.0/tests/test_mcp_host.py +907 -0
- pi_python_core-0.10.0/tests/test_mcp_interaction.py +872 -0
- pi_python_core-0.10.0/tests/test_mcp_process.py +237 -0
- pi_python_core-0.10.0/tests/test_nested_agents.py +273 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_plugins.py +378 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_providers.py +48 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_sync.py +73 -0
- pi_python_core-0.10.0/tests/test_tasks.py +158 -0
- pi_python_core-0.10.0/tests/test_typeddict_annotations.py +62 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_websocket_continuation.py +142 -0
- pi_python_core-0.9.0/src/pi_python/_version.py +0 -1
- pi_python_core-0.9.0/src/pi_python/sync.py +0 -78
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/.gitignore +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/LICENSE +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/NOTICE +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/COVERAGE.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/__init__.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/baseline.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/contracts/release-candidate.example.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/contracts/release-candidate.schema.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/contracts/upstream-baseline.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C01-text.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C02-tool.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C03-sequential.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C04-parallel.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C05-one-sequential.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C06-one-failure.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C07-unknown.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C08-prepare.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C09-before-error.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C09-block.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C09-replace.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C10-length.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C11-transform.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C12-system.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C13-steering.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C14-followup-all.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C14-followup-one.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C15-prepare.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C16-continue.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C16-end.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C16-mixed.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C16-terminate.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C19-abort-stream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C19-abort-tools.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/fixtures/C24-provider-error.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-cases.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/prompts/notes.txt +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/prompts/plan.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/prompts/review.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/prompts/sub/nested.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/prompts/summarize.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/.hidden/SKILL.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/alpha/SKILL.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/alpha/references/notes.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/beta/SKILL.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/broken/SKILL.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/delta/SKILL.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/gamma/SKILL.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/longdesc/SKILL.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/loose.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/nested/group/epsilon/SKILL.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/noname/SKILL.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/plain.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/zeta/SKILL.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/plugin-fixtures/skills/zeta/sub/eta/SKILL.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/anthropic-oauth.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/anthropic-request-cache-long.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/anthropic-request-cache-none.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/anthropic-request-replay.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/anthropic-request-tools-image.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/anthropic-text.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/anthropic-thinking.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/anthropic-tool.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/codex-request-tools-image.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/codex-text.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-compat-strict.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-deepseek.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-format-ant-ling.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-format-baseten.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-format-string-thinking.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-format-together.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-format-zai.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-midconvo-tools.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-moonshot-usage.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-openai-off.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-openrouter.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-qwen-length.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-reasoning-content.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-request-replay.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-text.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/completions-tool-stream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/openai-incomplete.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/openai-request-cache-long.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/openai-request-cache-none.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/openai-request-reasoning.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/openai-request-replay.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/openai-request-tools-image.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/openai-text.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/openai-thinking-backfill.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/openai-thinking.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/openai-tool.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/proxy-text.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/proxy-thinking-tool.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-anthropic-collapse-budget.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-anthropic-native-tools.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-anthropic-oauth-effort.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-anthropic-redefinition.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-anthropic-server-fallback.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-anthropic-thinking-off.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-codex-tool-search.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-openai-additional-tools.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-openai-collapse.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-openai-explicit-cache-long.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-openai-explicit-cache-none.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/provider-fixtures/session-openai-removal.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/recovery-cases.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/release.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C01-text.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C01-text.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C02-tool.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C02-tool.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C03-sequential.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C03-sequential.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C04-parallel.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C04-parallel.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C05-one-sequential.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C05-one-sequential.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C06-one-failure.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C06-one-failure.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C07-unknown.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C07-unknown.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C08-prepare.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C08-prepare.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C09-before-error.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C09-before-error.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C09-block.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C09-block.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C09-replace.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C09-replace.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C10-length.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C10-length.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C11-transform.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C11-transform.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C12-system.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C12-system.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C13-steering.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C13-steering.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C14-followup-all.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C14-followup-all.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C14-followup-one.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C14-followup-one.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C15-prepare.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C15-prepare.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C16-continue.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C16-continue.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C16-end.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C16-end.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C16-mixed.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C16-mixed.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C16-terminate.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C16-terminate.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C19-abort-stream.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C19-abort-stream.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C19-abort-tools.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C19-abort-tools.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C24-provider-error.python.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/C24-provider-error.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/artifacts.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/benchmark.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/bootstrap.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/candidate-live-noop.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/conformance.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/linux-3.11.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/linux-3.12.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/linux-3.13.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/linux-3.14.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/linux-install-hash-repair.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-0.5-regression.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-0.5-session-claude.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-0.5-session-codex.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-claude-reasoning-replay.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-final-matrix.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-initial.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-loop-initial.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-providers.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-reasoning-replay.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-refresh.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-regression-0.4-deepseek.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-regression-0.4-first-run.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-regression-0.4.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-session-claude.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-session-codex.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-websocket-cached-recheck.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-websocket-cached.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/live-websocket-final.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/plugins.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/provider-conformance.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/recovery-conformance.upstream.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/runtime-dependencies.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/source-verification.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.1.0/artifacts.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.1.0/linux-3.11.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.1.0/linux-3.12.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.1.0/linux-3.13.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.1.0/verification.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.2.0/artifacts.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.2.0/linux-3.11.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.2.0/linux-3.12.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.2.0/linux-3.13.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.2.0/verification.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.3.0/against-0.4-fixtures.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.3.0/artifacts.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.3.0/linux-3.11.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.3.0/linux-3.12.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.3.0/linux-3.13.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.3.0/provider-conformance.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.3.0/verification.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.4.0/artifacts.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.4.0/linux-3.11.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.4.0/linux-3.12.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.4.0/linux-3.13.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.4.0/verification.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.5.0/artifacts.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.5.0/linux-3.11.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.5.0/linux-3.12.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.5.0/linux-3.13.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.5.0/verification.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.6.0/artifacts.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.6.0/linux-3.11.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.6.0/linux-3.12.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.6.0/linux-3.13.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.6.0/verification.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.7.0/artifacts.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.7.0/linux-3.11.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.7.0/linux-3.12.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.7.0/linux-3.13.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.7.0/linux-3.14.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.7.0/verification.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.8.0/artifacts.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.8.0/linux-3.11.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.8.0/linux-3.12.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.8.0/linux-3.13.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.8.0/linux-3.14.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/v0.8.0/verification.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/verification.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/results/websocket-conformance.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/runner.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/websocket-fixtures/codex-continuation-lost-state.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/compat/websocket-fixtures/codex-continuation-tool-loop.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/CONCEPTS.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/IMPLEMENTATION.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/history/IMPLEMENTATION-0.3.0.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/history/IMPLEMENTATION-0.4.0.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/history/IMPLEMENTATION-0.5.0.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/history/IMPLEMENTATION-0.6.0.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/history/IMPLEMENTATION-0.7.0.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/history/IMPLEMENTATION-0.8.0.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/docs/zh/CONCEPTS.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/in_memory.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/local_model.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/mcp_tools.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/plugin_demo.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/plugins/lab_tools/agents/reviewer.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/plugins/lab_tools/ops.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/plugins/lab_tools/plugin.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/plugins/lab_tools/prompts/dedup.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/plugins/lab_tools/skills/dedup-window/SKILL.md +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/provider_chat.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/quickstart.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/recovery.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/examples/save_restore.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/cancellation.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/data/models.json +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/errors.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/estimate.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/events.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/hooks.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/limits.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/lowlevel.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/messages.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/models.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/plugins/_compose.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/plugins/_frontmatter.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/plugins/_resources.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/provider.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/providers/__init__.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/providers/common.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/proxy.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/py.typed +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/queues.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/recovery.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/stream.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/testing.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/src/pi_python/transcript.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_conformance.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_control.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_extended_core.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_loop.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_messages.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_models.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_oauth.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_plugin_conformance.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_recovery.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_recovery_conformance.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_recovery_helpers.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_release.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_session_changes.py +0 -0
- {pi_python_core-0.9.0 → pi_python_core-0.10.0}/tests/test_tools.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pi-python-core
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.10.0
|
|
4
4
|
Summary: An embeddable asyncio agent core with opt-in Claude and OpenAI providers
|
|
5
5
|
License-Expression: MIT
|
|
6
6
|
License-File: LICENSE
|
|
@@ -23,6 +23,9 @@ Requires-Dist: jsonschema<5,>=4.18
|
|
|
23
23
|
Requires-Dist: websockets>=14.2
|
|
24
24
|
Provides-Extra: mcp
|
|
25
25
|
Requires-Dist: mcp>=1.10; extra == 'mcp'
|
|
26
|
+
Provides-Extra: mcp-interactive
|
|
27
|
+
Requires-Dist: jsonschema[format-nongpl]<5,>=4.18; extra == 'mcp-interactive'
|
|
28
|
+
Requires-Dist: mcp<3,>=2.3; extra == 'mcp-interactive'
|
|
26
29
|
Provides-Extra: oauth
|
|
27
30
|
Requires-Dist: pyjwt[crypto]<3,>=2.8; extra == 'oauth'
|
|
28
31
|
Provides-Extra: providers
|
|
@@ -37,6 +40,9 @@ An embeddable Python agent core, ported from [Pi](https://github.com/earendil-wo
|
|
|
37
40
|
|
|
38
41
|
`pi-agent-core` on PyPI is a separate project that ports an older version from pi-mono (early 2026). This library follows the behavior of Pi v1.0.0, compared case by case with the upstream code's actual output, and ships its own connectors for Claude, OpenAI, DeepSeek and local models, with no model SDK required.
|
|
39
42
|
|
|
43
|
+
**Using a coding agent?** Start with the [library integration guide](docs/CODING_AGENTS.md).
|
|
44
|
+
To build an extension, follow the [plugin development workflow and complete example](docs/PLUGIN_DEVELOPMENT.md).
|
|
45
|
+
|
|
40
46
|
## Install
|
|
41
47
|
|
|
42
48
|
Python 3.11–3.14 (including free-threaded 3.14t) and PyPy 3.11. No Node.js or model SDK needed.
|
|
@@ -53,6 +59,7 @@ You install `pi-python-core` and import `pi_python`.
|
|
|
53
59
|
| `[oauth]` | Verifies the identity token when you sign in with a ChatGPT account (`openai-chatgpt`); it brings in the compiled `cryptography` package. Claude subscription and Codex sign-in do not need it |
|
|
54
60
|
| `[providers]` | Same as `[oauth]`; keeps install commands from 0.8.1 and earlier working |
|
|
55
61
|
| `[mcp]` | Gives the agent the tools of MCP servers, including those a plugin declares |
|
|
62
|
+
| `[mcp-interactive]` | Lets MCP services request host model calls and user forms; includes sampling profiles, metering and retries. See [MCP interaction](docs/MCP_INTERACTION.md) |
|
|
56
63
|
|
|
57
64
|
Dependencies are version ranges rather than pins, so the package fits into most existing environments.
|
|
58
65
|
|
|
@@ -107,6 +114,8 @@ A few differences are deliberate, such as strict tool-argument validation withou
|
|
|
107
114
|
|
|
108
115
|
## Documentation
|
|
109
116
|
|
|
117
|
+
- [Coding agents: integrate the library, run an offline example, and verify behavior](docs/CODING_AGENTS.md)
|
|
118
|
+
- [Plugin development: scaffold, readiness checks, packaging, and installed verification](docs/PLUGIN_DEVELOPMENT.md)
|
|
110
119
|
- [Concepts on one page: five ideas and one turn](docs/CONCEPTS.md)
|
|
111
120
|
- [Public API](docs/API.md)
|
|
112
121
|
- [Model connectors, subscription sign-in and local models](docs/PROVIDERS.md)
|
|
@@ -118,7 +127,7 @@ A few differences are deliberate, such as strict tool-argument validation withou
|
|
|
118
127
|
## Development
|
|
119
128
|
|
|
120
129
|
```bash
|
|
121
|
-
uv sync --locked --extra oauth --extra mcp
|
|
130
|
+
uv sync --locked --extra oauth --extra mcp-interactive
|
|
122
131
|
uv run pytest -q
|
|
123
132
|
uv run python scripts/verify.py # every check, including the comparison with upstream (needs Node)
|
|
124
133
|
```
|
|
@@ -6,6 +6,9 @@ An embeddable Python agent core, ported from [Pi](https://github.com/earendil-wo
|
|
|
6
6
|
|
|
7
7
|
`pi-agent-core` on PyPI is a separate project that ports an older version from pi-mono (early 2026). This library follows the behavior of Pi v1.0.0, compared case by case with the upstream code's actual output, and ships its own connectors for Claude, OpenAI, DeepSeek and local models, with no model SDK required.
|
|
8
8
|
|
|
9
|
+
**Using a coding agent?** Start with the [library integration guide](docs/CODING_AGENTS.md).
|
|
10
|
+
To build an extension, follow the [plugin development workflow and complete example](docs/PLUGIN_DEVELOPMENT.md).
|
|
11
|
+
|
|
9
12
|
## Install
|
|
10
13
|
|
|
11
14
|
Python 3.11–3.14 (including free-threaded 3.14t) and PyPy 3.11. No Node.js or model SDK needed.
|
|
@@ -22,6 +25,7 @@ You install `pi-python-core` and import `pi_python`.
|
|
|
22
25
|
| `[oauth]` | Verifies the identity token when you sign in with a ChatGPT account (`openai-chatgpt`); it brings in the compiled `cryptography` package. Claude subscription and Codex sign-in do not need it |
|
|
23
26
|
| `[providers]` | Same as `[oauth]`; keeps install commands from 0.8.1 and earlier working |
|
|
24
27
|
| `[mcp]` | Gives the agent the tools of MCP servers, including those a plugin declares |
|
|
28
|
+
| `[mcp-interactive]` | Lets MCP services request host model calls and user forms; includes sampling profiles, metering and retries. See [MCP interaction](docs/MCP_INTERACTION.md) |
|
|
25
29
|
|
|
26
30
|
Dependencies are version ranges rather than pins, so the package fits into most existing environments.
|
|
27
31
|
|
|
@@ -76,6 +80,8 @@ A few differences are deliberate, such as strict tool-argument validation withou
|
|
|
76
80
|
|
|
77
81
|
## Documentation
|
|
78
82
|
|
|
83
|
+
- [Coding agents: integrate the library, run an offline example, and verify behavior](docs/CODING_AGENTS.md)
|
|
84
|
+
- [Plugin development: scaffold, readiness checks, packaging, and installed verification](docs/PLUGIN_DEVELOPMENT.md)
|
|
79
85
|
- [Concepts on one page: five ideas and one turn](docs/CONCEPTS.md)
|
|
80
86
|
- [Public API](docs/API.md)
|
|
81
87
|
- [Model connectors, subscription sign-in and local models](docs/PROVIDERS.md)
|
|
@@ -87,7 +93,7 @@ A few differences are deliberate, such as strict tool-argument validation withou
|
|
|
87
93
|
## Development
|
|
88
94
|
|
|
89
95
|
```bash
|
|
90
|
-
uv sync --locked --extra oauth --extra mcp
|
|
96
|
+
uv sync --locked --extra oauth --extra mcp-interactive
|
|
91
97
|
uv run pytest -q
|
|
92
98
|
uv run python scripts/verify.py # every check, including the comparison with upstream (needs Node)
|
|
93
99
|
```
|
|
@@ -6,6 +6,9 @@
|
|
|
6
6
|
|
|
7
7
|
PyPI 上的 `pi-agent-core` 是另一个独立项目,移植的是 2026 年初 pi-mono 中的旧版本。本库对照 Pi v1.0.0 的行为,与上游的实际运行结果逐组比较;并自带 Claude、OpenAI、DeepSeek 和本地模型的接入,不需要安装任何模型 SDK。
|
|
8
8
|
|
|
9
|
+
**使用 coding agent 开发?** 先读 [library 接入指南](docs/zh/CODING_AGENTS.md)。
|
|
10
|
+
需要扩展能力时,按 [插件开发流程与完整示例](docs/zh/PLUGIN_DEVELOPMENT.md) 实现、打包和验证。
|
|
11
|
+
|
|
9
12
|
## 安装
|
|
10
13
|
|
|
11
14
|
支持 Python 3.11–3.14(包括无 GIL 的 3.14t)和 PyPy 3.11,不需要 Node 或任何模型 SDK。
|
|
@@ -22,6 +25,7 @@ pip install pi-python-core # 或 uv add pi-python-core
|
|
|
22
25
|
| `[oauth]` | 用 ChatGPT 账号登录(`openai-chatgpt`)时校验身份令牌,会带进需要编译的 `cryptography`。Claude 订阅和 Codex 登录不需要它 |
|
|
23
26
|
| `[providers]` | 与 `[oauth]` 相同,让 0.8.1 及以前的安装命令仍然可用 |
|
|
24
27
|
| `[mcp]` | 把 MCP 服务器的工具交给 agent,插件声明的 MCP 服务器也需要它 |
|
|
28
|
+
| `[mcp-interactive]` | 允许 MCP 服务请求宿主模型调用及用户表单,支持配置选择、计量与重试。见 [MCP 交互](docs/zh/MCP_INTERACTION.md) |
|
|
25
29
|
|
|
26
30
|
依赖写的是版本范围而不是固定版本,能和大多数已有环境共存。
|
|
27
31
|
|
|
@@ -76,6 +80,8 @@ agent = Agent(provider=llm, model=llm.model("qwen3:8b", context_window=40960), t
|
|
|
76
80
|
|
|
77
81
|
## 文档
|
|
78
82
|
|
|
83
|
+
- [Coding agents:接入 library、运行离线示例、验证行为](docs/zh/CODING_AGENTS.md)
|
|
84
|
+
- [插件开发:目录结构、就绪检查、打包与安装验证](docs/zh/PLUGIN_DEVELOPMENT.md)
|
|
79
85
|
- [一页看懂:五个概念和一轮的流程](docs/zh/CONCEPTS.md)
|
|
80
86
|
- [公开 API](docs/zh/API.md)
|
|
81
87
|
- [模型接入、订阅登录与本地模型](docs/zh/PROVIDERS.md)
|
|
@@ -87,7 +93,7 @@ agent = Agent(provider=llm, model=llm.model("qwen3:8b", context_window=40960), t
|
|
|
87
93
|
## 开发
|
|
88
94
|
|
|
89
95
|
```bash
|
|
90
|
-
uv sync --locked --extra oauth --extra mcp
|
|
96
|
+
uv sync --locked --extra oauth --extra mcp-interactive
|
|
91
97
|
uv run pytest -q
|
|
92
98
|
uv run python scripts/verify.py # 全部检查,含与上游的差分(需要 Node)
|
|
93
99
|
```
|
|
@@ -48,6 +48,8 @@ Every event passes the same check: blocks must pair up, deltas must land in an o
|
|
|
48
48
|
|
|
49
49
|
Message data also allows `pending` and `deferred`. `pending` is only for in-stream snapshots and cannot be committed to history; `deferred` can hold a background handle, but the library does not yet poll background tasks. For `length`, every tool call gets a not-executed result, and the model may then handle the error. `error` / `aborted` messages cannot declare tool calls: before the message is committed, its tool calls are removed, the rest of the partial content is kept, and a `removed_tool_calls` entry is added to `diagnostics`.
|
|
50
50
|
|
|
51
|
+
The Agent closes each request's response iterator when the request ends, including error and cancellation paths. Custom iterators that own resources should implement `aclose()`. This does not close the shared Provider.
|
|
52
|
+
|
|
51
53
|
## Tool
|
|
52
54
|
|
|
53
55
|
`Tool(name, description, input_schema, execute, output_schema=None, execution_mode="parallel", prepare_arguments=None)`.
|
|
@@ -81,8 +83,34 @@ def search_papers(query: str, year: int | None = None, limit: int = 10) -> list[
|
|
|
81
83
|
|
|
82
84
|
Supported parameter types: `str`, `int`, `float`, `bool`, `None`, `list`, `set`, `tuple`, `dict[str, T]`, `Literal`, `Enum`, `Optional` and other unions, `Annotated[T, "description"]`, `TypedDict`, dataclasses, `datetime`, `date`, `UUID`, `Path`, and pydantic models. Before the call, the JSON arguments are converted to the types the function asks for, such as enum members, dates, dataclasses or pydantic models. A parameter annotated as `ToolContext` receives the call context and does not appear in the schema. `*args`, `**kwargs` and types that cannot be represented as JSON raise `ConfigurationError` at registration.
|
|
83
85
|
|
|
86
|
+
For a union, conversion tries branches in annotation order, using only branches whose JSON Schema matches the input. For example, `list[int] | str` preserves `"abc"` as a string. When several branches match, the first successful conversion wins.
|
|
87
|
+
|
|
88
|
+
Schema and conversion are compiled together at registration, including recursive types and union branches. Bare `set`, `frozenset` and `tuple` annotations also convert JSON arrays to the requested Python container. Nested pydantic definitions are isolated between parameters and union branches, even when their class names match.
|
|
89
|
+
|
|
90
|
+
`TypedDict` requiredness follows resolved `Required` and `NotRequired` annotations,
|
|
91
|
+
including inherited fields and `Annotated` wrappers when annotations are postponed.
|
|
92
|
+
|
|
84
93
|
Concurrency is unlimited by default: a batch of tools all run at once, as in Pi; set `RunLimits(max_concurrency=...)` when you need a cap. If any tool requires `sequential`, the whole batch runs one at a time. When running concurrently, preparation completes in call order before execution starts; end events follow the order in which post-processing finishes, and results in history follow call order.
|
|
85
94
|
|
|
95
|
+
Parallel tool and subagent batches own their child tasks: failure or cancellation cancels unfinished siblings and waits for their cleanup. Repeated caller cancellation does not interrupt that cleanup. The Run's existing cleanup deadline still applies; `cleanup_complete` reports whether cleanup actually finished.
|
|
96
|
+
|
|
97
|
+
Inside a tool, use `await context.run_agent(child, message)` to run an exclusively
|
|
98
|
+
owned, idle child Agent. It returns the child's `RunResult` and closes that Agent;
|
|
99
|
+
the shared Provider stays open. See [the subagent example](../examples/subagent.py).
|
|
100
|
+
An already running child is rejected without cancelling or closing it. Ordinary
|
|
101
|
+
model/tool failures remain results that the calling tool can handle.
|
|
102
|
+
|
|
103
|
+
Nested calls preserve two separate facts: whether an external operation's outcome
|
|
104
|
+
is known, and whether its work has stopped. An unknown child outcome raises
|
|
105
|
+
`ToolOutcomeUnknownError`, marks the enclosing Agent as requiring reconciliation,
|
|
106
|
+
and stops further execution. This also applies across parallel siblings and multiple
|
|
107
|
+
nesting levels. An unfinished child keeps its enclosing tool pending. The parent's
|
|
108
|
+
cleanup deadline still bounds its run, which reports `cleanup_complete=False` until
|
|
109
|
+
the descendant actually finishes. Repeated cancellation cannot abandon that cleanup.
|
|
110
|
+
Standalone `ToolContext` callers must keep awaiting the call or manage its lifetime
|
|
111
|
+
with `TaskScope`; without an enclosing Agent there is no Run cleanup deadline.
|
|
112
|
+
Check `require_features(["nested-agent-ownership-v1"])` when depending on this API.
|
|
113
|
+
|
|
86
114
|
A standalone program can call `await run_tool_call(tool, call, context, before_tool_call=..., after_tool_call=...)` to reuse the same validation and hook path. For standalone calls, the program manages lifetime, timeouts and cancellation itself; `Agent` adds batch management and `RunLimits`.
|
|
87
115
|
|
|
88
116
|
## Hooks
|
|
@@ -159,6 +187,23 @@ async with connect_stdio("uvx", ["mcp-server-fetch"], prefix="web") as tools:
|
|
|
159
187
|
|
|
160
188
|
Requires `pip install 'pi-python-core[mcp]'` and works with the official MCP SDK 1.10 and later, including 2.x. `connect_stdio` starts a stdio MCP server and closes it when the `async with` block exits. `connect_http(url, headers=None, prefix=None, names=None)` connects to a streamable HTTP server in the same way; the legacy SSE transport is not supported, as in Pi. It follows redirects only within the server's origin, so headers are never sent to another one. It is tested with MCP SDK 1.10, 1.30 and 2.3. If you already have a `ClientSession`, wrap its tools with `await mcp_tools(session, prefix=None, names=None)`. Conversion follows Pi's MCP adapter: text and images convert directly; embedded text or image resources are unpacked; audio, resource links and binary resources become short text descriptions; a result with only structured content becomes JSON text and is also kept as `structured_content`; MCP's `isError` becomes a tool error; progress notifications become `tool_execution_update` events. After the prefix is added, tool names keep only letters, digits, `_` and `-`, up to 64 characters. A tool whose schema cannot be used is skipped with a warning, without affecting the server's other tools. A stdio server written with Python MCP SDK 2.3 cannot start on PyPy (`fcntl.F_DUPFD_CLOEXEC` is missing); this is a limitation of the SDK itself, and the client side is unaffected.
|
|
161
189
|
|
|
190
|
+
On POSIX, `connect_stdio(..., process_scope=True)` also cleans surviving process
|
|
191
|
+
groups and nested pi-python stdio connections on exit. It is opt-in at the outer
|
|
192
|
+
connection and inherited by nested connections. Non-POSIX systems reject explicit
|
|
193
|
+
enablement. See [process ownership](MCP_INTERACTION.md) for scope and limitations.
|
|
194
|
+
|
|
195
|
+
All three MCP entry points accept optional `call_metadata`, a JSON object passed to
|
|
196
|
+
`ClientSession.call_tool(meta=...)`. It is copied per registration and call, stays out
|
|
197
|
+
of tool arguments, and requires an SDK with `meta` support; otherwise registration
|
|
198
|
+
raises `ConfigurationError`. See [runtime metadata and readiness](PLUGINS.md#mcp-servers)
|
|
199
|
+
for plugin configuration and startup checks.
|
|
200
|
+
|
|
201
|
+
`connect_stdio` and `connect_http` also accept `callbacks=MCPCallbacks(...)` for
|
|
202
|
+
host-authorized sampling and form elicitation. `SamplingHandler` adapts a host Provider;
|
|
203
|
+
`SamplingProvider` runs a server Agent through the current MCP request. This opt-in
|
|
204
|
+
feature requires SDK >=2.3,<3. See [MCP interaction](MCP_INTERACTION.md) for public APIs,
|
|
205
|
+
protocol constraints, cancellation, and the runnable offline example.
|
|
206
|
+
|
|
162
207
|
## Plugins
|
|
163
208
|
|
|
164
209
|
`pi_python.plugins` loads plugins: named bundles of tools, system prompt text, hooks, skills, prompt templates, subagents and MCP servers, in the format of Pi's packages. `async with load_plugins([...], services=..., options=...) as plugins:` loads them, and `plugins.agent(...)` builds an Agent with everything they contribute. The [plugin guide](PLUGINS.md) covers writing, distributing and combining plugins.
|
|
@@ -216,3 +261,45 @@ OpenAI may add encrypted_content back in the final response. If the visible reas
|
|
|
216
261
|
`AssistantMessage.provider_thinking_level` matches upstream: it records this answer's effort only for Claude models that use effort markers, so later requests can rebuild the markers in history. Read `thinking_level` when you need the requested level.
|
|
217
262
|
|
|
218
263
|
When replaying history, Providers skip `error` / `aborted` answers following upstream's rules; reasoning from a different model is converted to plain text and its signature is dropped; a system message that sits between a tool call and its result is moved after the result. The session history itself is unchanged.
|
|
264
|
+
|
|
265
|
+
Host sampling profiles, usage observation, retry policy, and build feature markers are documented in [MCP interactions](MCP_INTERACTION.md#real-providers-profiles-accounting-and-retries).
|
|
266
|
+
|
|
267
|
+
|
|
268
|
+
## Owned tasks and calls from worker threads
|
|
269
|
+
|
|
270
|
+
`TaskScope` owns the asynchronous calls passed to `await scope.run(awaitable,
|
|
271
|
+
cancel=token)`. Each call runs in a child task. Cancelling the caller or token
|
|
272
|
+
cancels that task and waits for its cleanup. `await scope.aclose()` rejects new
|
|
273
|
+
calls, cancels active calls, and joins them, including calls waiting on an
|
|
274
|
+
application lock. Use `async with TaskScope() as scope`, or a plugin's
|
|
275
|
+
`api.task_scope()` to register automatic cleanup. Exceptions go to the individual
|
|
276
|
+
caller. Closing does not re-raise a completed call's error. Work must cooperate
|
|
277
|
+
with cancellation; this API does not terminate threads or impose a deadline.
|
|
278
|
+
An owned task cannot close its own scope. A scope is bound to its first event loop.
|
|
279
|
+
|
|
280
|
+
`LoopPortal` submits async work from a synchronous worker thread onto an **existing**
|
|
281
|
+
event loop. This differs from `run_sync`, which owns a separate background loop.
|
|
282
|
+
Create and close the portal on the loop that owns the connection:
|
|
283
|
+
|
|
284
|
+
```python
|
|
285
|
+
import asyncio
|
|
286
|
+
from pi_python import LoopPortal
|
|
287
|
+
|
|
288
|
+
async def request(connection):
|
|
289
|
+
async with LoopPortal() as portal:
|
|
290
|
+
# A callable is passed, not a coroutine created in the worker thread.
|
|
291
|
+
return await asyncio.to_thread(portal.call, connection.fetch, "item")
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
`portal.call(async_function, *args, timeout=None, **kwargs)` returns the result or
|
|
295
|
+
raises the error in the worker thread, preserving its context variables. Blocking
|
|
296
|
+
an event-loop thread is rejected. Closing cancels and joins submitted work without
|
|
297
|
+
closing the borrowed loop. A timeout bounds the blocking wait and requests async
|
|
298
|
+
cancellation; `aclose()` waits for that cleanup. Keep the owning loop running until
|
|
299
|
+
the portal has closed. Application threads themselves remain application-owned.
|
|
300
|
+
|
|
301
|
+
`FEATURES` is the immutable set of versioned APIs provided by the current build.
|
|
302
|
+
`require_features(names, where="Application")` raises `ConfigurationError` for
|
|
303
|
+
unknown or unavailable features. It does not validate installed optional packages,
|
|
304
|
+
MCP peer capabilities, credentials, or host authorization. `MCP_FEATURES` remains
|
|
305
|
+
available and is a subset. Plugin declarations are described in [Plugins](PLUGINS.md#declaring-required-framework-features).
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Using pi-python-core with a coding agent
|
|
2
|
+
|
|
3
|
+
**English** | [中文](zh/CODING_AGENTS.md)
|
|
4
|
+
|
|
5
|
+
Use this guide when building an application **with** this library. Start with the
|
|
6
|
+
offline tool call below, then replace its model provider. For a reusable extension,
|
|
7
|
+
follow [Plugin development](PLUGIN_DEVELOPMENT.md). Repository maintenance commands
|
|
8
|
+
are at the end; they are not required in a consuming application.
|
|
9
|
+
|
|
10
|
+
## 1. Establish the environment
|
|
11
|
+
|
|
12
|
+
The distribution is `pi-python-core`; the import is `pi_python`. Python 3.11 or later
|
|
13
|
+
is required. The library uses asyncio. Import public APIs from `pi_python`,
|
|
14
|
+
`pi_python.providers`, `pi_python.plugins`, or `pi_python.mcp`, rather than private
|
|
15
|
+
modules whose names start with `_`.
|
|
16
|
+
|
|
17
|
+
In a consuming project, install with `uv add pi-python-core` or
|
|
18
|
+
`python -m pip install pi-python-core`. Optional extras are `[mcp]` for MCP tools,
|
|
19
|
+
`[mcp-interactive]` for host sampling/forms, and `[oauth]` for ChatGPT identity-token
|
|
20
|
+
verification. Built-in model connectors do not require an extra.
|
|
21
|
+
|
|
22
|
+
When working from this checkout, run these commands from the repository root:
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
uv sync --locked --extra mcp-interactive
|
|
26
|
+
uv run python examples/quickstart.py
|
|
27
|
+
uv run python examples/plugin_demo.py
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
These examples are offline and need no credentials. The docs describe the checked-out
|
|
31
|
+
code; its version may not yet be published. Inspect `pyproject.toml` and check the
|
|
32
|
+
installed version with `python -c "from importlib.metadata import version; print(version('pi-python-core'))"`.
|
|
33
|
+
For another project to use this checkout, install it in that project's environment
|
|
34
|
+
with `python -m pip install -e /absolute/path/to/pi-python` (append
|
|
35
|
+
`[mcp-interactive]` to that path if needed).
|
|
36
|
+
|
|
37
|
+
## 2. Run a complete offline tool call
|
|
38
|
+
|
|
39
|
+
Save this as `app.py` and run `python app.py` in the installed environment, or
|
|
40
|
+
`uv run python app.py` in this checkout. It prints `3 words.` and checks the actual
|
|
41
|
+
tool result, not just the model's scripted answer.
|
|
42
|
+
|
|
43
|
+
```python
|
|
44
|
+
import asyncio
|
|
45
|
+
|
|
46
|
+
from pi_python import (
|
|
47
|
+
Agent, AssistantMessage, RunLimits, ScriptedProvider, TextContent, ToolCall, tool,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
@tool
|
|
52
|
+
def count_words(text: str) -> dict[str, int]:
|
|
53
|
+
"""Count whitespace-separated words in text."""
|
|
54
|
+
return {"words": len(text.split())}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
async def main() -> None:
|
|
58
|
+
provider = ScriptedProvider([
|
|
59
|
+
AssistantMessage([
|
|
60
|
+
ToolCall("count-1", "count_words", {"text": "one two three"}),
|
|
61
|
+
], stop_reason="tool_use"),
|
|
62
|
+
AssistantMessage.text("3 words."),
|
|
63
|
+
])
|
|
64
|
+
async with Agent(
|
|
65
|
+
provider=provider,
|
|
66
|
+
tools=[count_words],
|
|
67
|
+
limits=RunLimits(max_model_requests=4, max_tool_calls=4, run_timeout=30),
|
|
68
|
+
) as agent:
|
|
69
|
+
result = await agent.prompt("Count the words in: one two three")
|
|
70
|
+
if result.status != "completed":
|
|
71
|
+
raise RuntimeError(f"{result.status}: {result.errors}")
|
|
72
|
+
outcome = result.tool_outcomes[0]
|
|
73
|
+
assert outcome.execution_status == "succeeded"
|
|
74
|
+
assert outcome.result.structured_content == {"words": 3}
|
|
75
|
+
answer = result.messages[-1]
|
|
76
|
+
assert isinstance(answer, AssistantMessage)
|
|
77
|
+
print("".join(b.text for b in answer.content if isinstance(b, TextContent)))
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
if __name__ == "__main__":
|
|
81
|
+
asyncio.run(main())
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`Agent` owns the conversation and tool loop. A `Provider` supplies model responses.
|
|
85
|
+
`@tool` turns a typed function and its docstring into a tool declaration. The model
|
|
86
|
+
chooses tool names and JSON arguments; the library validates arguments and executes
|
|
87
|
+
the functions. `ScriptedProvider` fixes those choices for a deterministic test. It
|
|
88
|
+
does not demonstrate that a real model will choose the same tool or answer.
|
|
89
|
+
|
|
90
|
+
## 3. Replace the provider, keep application ownership explicit
|
|
91
|
+
|
|
92
|
+
For a real model, pass the application's credentials and chosen model explicitly.
|
|
93
|
+
Inside an async function, the replacement looks like this (it makes a paid/network
|
|
94
|
+
call and is not part of the offline example):
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
import os
|
|
98
|
+
from pi_python.providers import AnthropicProvider
|
|
99
|
+
|
|
100
|
+
provider = AnthropicProvider(api_key=os.environ["ANTHROPIC_API_KEY"])
|
|
101
|
+
try:
|
|
102
|
+
async with Agent(
|
|
103
|
+
provider=provider,
|
|
104
|
+
model=os.environ["ANTHROPIC_MODEL"],
|
|
105
|
+
tools=[count_words],
|
|
106
|
+
limits=RunLimits(max_model_requests=4, run_timeout=60),
|
|
107
|
+
) as agent:
|
|
108
|
+
result = await agent.prompt("Count the words in: one two three")
|
|
109
|
+
if result.status != "completed":
|
|
110
|
+
raise RuntimeError(f"{result.status}: {result.errors}")
|
|
111
|
+
finally:
|
|
112
|
+
await provider.aclose()
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
`Agent.aclose()` closes the agent's work, not a shared provider. The application
|
|
116
|
+
closes its provider after all agents using it are done. Keep creation, use and
|
|
117
|
+
cleanup on the same event loop. For another connector, use
|
|
118
|
+
[Providers](PROVIDERS.md) and the runnable [local-model example](../examples/local_model.py).
|
|
119
|
+
|
|
120
|
+
## 4. Observe the contracts when writing application code
|
|
121
|
+
|
|
122
|
+
| Concern | Contract to follow |
|
|
123
|
+
|---|---|
|
|
124
|
+
| Async versus sync | In servers/notebooks use `await agent.prompt(...)`. In a plain synchronous script use `agent.prompt_sync(...)`. Do not call a blocking API on an event-loop thread or repeatedly wrap a loop-bound provider in separate `asyncio.run` calls. |
|
|
125
|
+
| Conversation ownership | One agent accepts one active run. Await turns in order; use separate agents for independent conversations. `agent.state` is a copy. |
|
|
126
|
+
| Completion | Check `result.status`: `completed`, `failed`, `cancelled`, or `limit_reached`. Model/tool failures are not all raised as Python exceptions. Inspect `errors` and `tool_outcomes` before accepting a business result. |
|
|
127
|
+
| Tool inputs | Give functions typed parameters and clear docstrings. A `ToolContext` parameter is injected and omitted from the model schema. Arguments are validated without JSON type coercion. |
|
|
128
|
+
| Tool outputs | Return JSON-compatible values or `ToolResult`. Dicts/numbers are available as `structured_content` and text. `details` is application data; it is not automatically sent to the model. |
|
|
129
|
+
| Concurrency and limits | Tool batches run in parallel by default. Set `RunLimits`; use `@tool(execution_mode="sequential")` for tools that require a sequential batch. Plain functions run in worker threads; cancellation cannot forcibly stop those threads. |
|
|
130
|
+
| Cancellation | Use `agent.abort()` and await completion/cleanup. Check `cleanup_complete` and `reconciliation_required` before reusing an agent or retrying a side effect. Prefer async tools for cancellable I/O. |
|
|
131
|
+
| Recovery | `continue_run()` can retry a failed model turn without replaying past tools. It is not a general retry mechanism for failed business operations. See [recovery](../examples/recovery.py). |
|
|
132
|
+
| Persistence | Store `encode_messages(list(agent.state.messages))`; restore with `decode_messages` and `Agent(messages=...)`. `result.messages` contains only the current run's additions. See [save/restore](../examples/save_restore.py). |
|
|
133
|
+
| Streaming | `agent.subscribe(listener)` receives `message_update` events with `delta_type` and `delta`. Critical listeners are awaited, so keep them bounded. See [quickstart](../examples/quickstart.py). |
|
|
134
|
+
|
|
135
|
+
For application-owned async work use `TaskScope`. To call an existing async connection
|
|
136
|
+
from a synchronous tool's worker thread, use `LoopPortal` created on its owning loop.
|
|
137
|
+
Do not move that connection to `run_sync`'s separate loop. See
|
|
138
|
+
[task ownership and thread calls](API.md#owned-tasks-and-calls-from-worker-threads).
|
|
139
|
+
These APIs and feature declarations are part of the 0.10.0 source line; check
|
|
140
|
+
`require_features(["task-scope-v1", "loop-portal-v1"])` when depending on them.
|
|
141
|
+
|
|
142
|
+
## 5. Choose the smallest integration surface
|
|
143
|
+
|
|
144
|
+
| Need | Start here |
|
|
145
|
+
|---|---|
|
|
146
|
+
| A few tools in one application | `Agent(..., tools=[...])`; no plugin is necessary. |
|
|
147
|
+
| Reusable tools, instructions and resources | [Plugin development workflow](PLUGIN_DEVELOPMENT.md), then [plugin API](PLUGINS.md). |
|
|
148
|
+
| Tools from an MCP server | [MCP tools example](../examples/mcp_tools.py); keep the connection open for the agent's lifetime. |
|
|
149
|
+
| MCP server requests a host model or form | [MCP interactions](MCP_INTERACTION.md) and [offline example](../examples/mcp_interactive.py). Use the interactive extra and explicit host callbacks. |
|
|
150
|
+
| A nested agent | Use `await context.run_agent(child, message)` inside a tool to preserve cleanup ownership and unknown outcomes; see the [subagent example](../examples/subagent.py). Plugins can supply definitions in `agents/`. |
|
|
151
|
+
| Your own model adapter | The [Provider contract](API.md#provider); emit exactly one terminal `ModelEvent.done` and honor cancellation. |
|
|
152
|
+
|
|
153
|
+
MCP incremental sampling is a negotiated extension. It reconstructs the full request
|
|
154
|
+
before host policy and model access. It reduces repeated MCP transmission, not the
|
|
155
|
+
model's history or token count; business code should not manage its internal cache.
|
|
156
|
+
|
|
157
|
+
## 6. Verify before handing off
|
|
158
|
+
|
|
159
|
+
For a consuming application, run an offline tool-call test, assert the actual tool
|
|
160
|
+
result, exercise failure/cancellation when relevant, and then test the configured real
|
|
161
|
+
provider separately. Report which paths ran. For plugins, also verify the installed
|
|
162
|
+
wheel using the [plugin workflow](PLUGIN_DEVELOPMENT.md).
|
|
163
|
+
|
|
164
|
+
When changing this library itself, read the applicable `AGENTS.md`, follow its scope,
|
|
165
|
+
and run the relevant tests. The repository checks are:
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
uv run ruff check src tests examples compat scripts
|
|
169
|
+
uv run ruff format --check src tests examples compat scripts
|
|
170
|
+
uv run mypy src/pi_python
|
|
171
|
+
uv run pytest -q
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Use [API](API.md) for exact contracts, [concepts](CONCEPTS.md) for the execution model,
|
|
175
|
+
and [reference verification](../reference/README.md) when a change affects upstream
|
|
176
|
+
compatibility. Do not invent methods or assume a different agent framework's APIs apply.
|