zai-adk-python-preview 0.1.2__tar.gz → 0.1.4__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.
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/.gitignore +4 -1
- zai_adk_python_preview-0.1.4/.gitlab-ci.yml +10 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/PKG-INFO +46 -1
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/README.md +45 -0
- zai_adk_python_preview-0.1.4/examples/test_streaming.py +110 -0
- zai_adk_python_preview-0.1.4/examples/test_streaming_real.py +450 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/pyproject.toml +1 -1
- zai_adk_python_preview-0.1.4/tests/test_streaming_e2e.py +304 -0
- zai_adk_python_preview-0.1.4/tests/unit/agui/test_streaming_converter.py +98 -0
- zai_adk_python_preview-0.1.4/tests/unit/llm/test_stream.py +103 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/uv.lock +2 -2
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/agent/events.py +51 -1
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/agent/service.py +82 -13
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/llm/anthropic/chat.py +87 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/llm/base.py +35 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/llm/openai/chat.py +140 -45
- zai_adk_python_preview-0.1.4/zai_adk/llm/stream.py +109 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/protocols/agui/converter.py +68 -13
- zai_adk_python_preview-0.1.2/.claude/settings.local.json +0 -37
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/CLAUDE.md +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/LICENSE +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/README_zh.md +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/docs/agui.md +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/docs/plans/2025-02-02-reorganize-tests.md +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/docs/plans/2025-02-02-skill-reference-fix.md +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/docs/plans/2026-01-29-sandbox.md +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/docs/plans/2026-01-30-built-in-tools.md +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/docs/plans/2026-01-30-skill-filesystem-enhancement.md +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/docs/plans/2026-02-02-skills-optimization.md +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/docs/plans/2026-02-02-subagent-implementation.md +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/docs/plans/2026-02-02-test-reorganization.md +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/docs/plans/2026-02-03-ag-ui-protocol-support.md +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/docs/plans/2026-02-03-default-sandbox.md +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/docs/plans/2026-02-04-agui-missing-events.md +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/docs/plans/2026-02-04-protocol-layer-refactor.md +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/docs/plans/2026-02-04-remove-agui-from-agent.md +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/docs/protocols.md +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/docs/sandbox.md +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/examples/claude_code.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/README.md +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/conftest.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/integration/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/integration/agent/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/integration/agent/test_agent_query.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/integration/agent/test_default_sandbox_integration.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/integration/agent/test_subagent_integration.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/integration/agui/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/integration/agui/test_agui_integration.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/integration/llm/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/integration/llm/test_anthropic_chat.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/integration/llm/test_openai_chat.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/integration/skills/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/integration/skills/test_skills_end_to_end.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/integration/skills/test_skills_live.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/integration/tools/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/integration/tools/test_tools_integration.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/agent/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/agent/test_agent.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/agent/test_agent_sandbox.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/agent/test_agent_template.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/agent/test_agent_templates.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/agent/test_async_initialization.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/agent/test_delegate_task.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/agent/test_dependencies.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/agent/test_sandbox_integration.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/agent/test_skills_hot_reload.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/agent/test_skills_injection.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/agent/test_skills_integration.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/agent/test_subagent_types.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/agent/test_subagent_unit.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/agui/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/agui/test_agent_integration.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/agui/test_converter.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/agui/test_encoder.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/agui/test_error_handling.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/agui/test_events.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/agui/test_protocol.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/agui/test_thinking_events.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/compaction/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/compaction/test_compaction.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/mcp/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/mcp/test_client.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/mcp/test_config.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/protocols/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/protocols/test_base.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/sandbox/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/sandbox/test_base.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/sandbox/test_factory.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/sandbox/test_local.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/sandbox/test_sandbox_config.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/skills/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/skills/test_manager.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/skills/test_manager_filesystem.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/skills/test_manager_logging.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/skills/test_parser.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/skills/test_types.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/skills/test_xml_generator.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/todo/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/todo/test_service.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/tokens/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/tokens/test_tokens.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/tools/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/tools/test_fs_edit.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/tools/test_fs_grep.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/tools/test_sandbox_tools.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/tools/test_skill_exec_tool.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/tools/test_skills_tool.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/tools/test_task_run_tool.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/tests/unit/tools/test_todo_tools.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/agent/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/agent/types.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/context/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/context/compaction.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/context/models.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/llm/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/llm/anthropic/serializer.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/llm/exceptions.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/llm/messages.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/llm/openai/serializer.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/llm/schema.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/llm/views.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/mcp/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/mcp/client.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/mcp/config.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/observability/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/observability/observability.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/protocols/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/protocols/agui/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/protocols/agui/encoder.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/protocols/agui/events.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/protocols/agui/protocol.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/protocols/base.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/sandbox/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/sandbox/base.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/sandbox/config.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/sandbox/factory.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/sandbox/local.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/skills/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/skills/manager.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/skills/parser.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/skills/types.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/skills/xml_generator.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/todo/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/todo/service.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/tokens/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/tokens/custom_pricing.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/tokens/mappings.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/tokens/service.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/tokens/views.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/tools/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/tools/builtin/__init__.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/tools/builtin/bash.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/tools/builtin/fs_edit.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/tools/builtin/fs_glob.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/tools/builtin/fs_grep.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/tools/builtin/fs_read.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/tools/builtin/fs_stat.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/tools/builtin/fs_write.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/tools/builtin/skill_exec.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/tools/builtin/skills.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/tools/builtin/task_run.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/tools/builtin/todo.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/tools/decorator.py +0 -0
- {zai_adk_python_preview-0.1.2 → zai_adk_python_preview-0.1.4}/zai_adk/tools/depends.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: zai-adk-python-preview
|
|
3
|
-
Version: 0.1.
|
|
3
|
+
Version: 0.1.4
|
|
4
4
|
Summary: A Python SDK for building AI agents with LLM integration
|
|
5
5
|
Project-URL: Homepage, https://github.com/zai-adk-python/zai-adk-python
|
|
6
6
|
Project-URL: Documentation, https://github.com/zai-adk-python/zai-adk-python#readme
|
|
@@ -139,6 +139,51 @@ async for event in agent.query_stream("Help me with a task"):
|
|
|
139
139
|
print(f"[Final]: {text}")
|
|
140
140
|
```
|
|
141
141
|
|
|
142
|
+
### True Token Streaming
|
|
143
|
+
|
|
144
|
+
Enable real-time token-by-token streaming from the LLM with `stream=True`:
|
|
145
|
+
|
|
146
|
+
```python
|
|
147
|
+
from zai_adk.agent import TextChunkEvent, ThinkingChunkEvent, FinalResponseEvent
|
|
148
|
+
|
|
149
|
+
async for event in agent.query_stream("Explain quantum computing", stream=True):
|
|
150
|
+
match event:
|
|
151
|
+
case ThinkingChunkEvent(content=delta):
|
|
152
|
+
print(delta, end="", flush=True) # thinking token delta
|
|
153
|
+
case TextChunkEvent(content=delta):
|
|
154
|
+
print(delta, end="", flush=True) # text token delta
|
|
155
|
+
case FinalResponseEvent(content=text):
|
|
156
|
+
print(f"\nDone: {text}")
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Key differences from default (`stream=False`):**
|
|
160
|
+
|
|
161
|
+
| | `stream=False` (default) | `stream=True` |
|
|
162
|
+
|---|---|---|
|
|
163
|
+
| First token latency | Waits for full response | Streams immediately |
|
|
164
|
+
| Event types | `TextEvent` (complete text) | `TextChunkEvent` per token + `TextEvent` (complete) |
|
|
165
|
+
| Thinking | `ThinkingEvent` (complete) | `ThinkingChunkEvent` per token + `ThinkingEvent` (complete) |
|
|
166
|
+
| Backward compat | — | Fully compatible, same tool execution flow |
|
|
167
|
+
|
|
168
|
+
**Enable extended thinking (GLM, DeepSeek):**
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
from zai_adk.llm.openai.chat import ChatOpenAI
|
|
172
|
+
|
|
173
|
+
llm = ChatOpenAI(
|
|
174
|
+
model="glm-5.1",
|
|
175
|
+
thinking={"type": "enabled"}, # enable reasoning output
|
|
176
|
+
)
|
|
177
|
+
agent = Agent(llm=llm, tools=[])
|
|
178
|
+
|
|
179
|
+
async for event in agent.query_stream("Think carefully: 9.11 vs 9.8", stream=True):
|
|
180
|
+
match event:
|
|
181
|
+
case ThinkingChunkEvent(content=delta):
|
|
182
|
+
print(f"[think] {delta}", end="", flush=True)
|
|
183
|
+
case TextChunkEvent(content=delta):
|
|
184
|
+
print(delta, end="", flush=True)
|
|
185
|
+
```
|
|
186
|
+
|
|
142
187
|
### Sandboxed File Operations
|
|
143
188
|
|
|
144
189
|
```python
|
|
@@ -89,6 +89,51 @@ async for event in agent.query_stream("Help me with a task"):
|
|
|
89
89
|
print(f"[Final]: {text}")
|
|
90
90
|
```
|
|
91
91
|
|
|
92
|
+
### True Token Streaming
|
|
93
|
+
|
|
94
|
+
Enable real-time token-by-token streaming from the LLM with `stream=True`:
|
|
95
|
+
|
|
96
|
+
```python
|
|
97
|
+
from zai_adk.agent import TextChunkEvent, ThinkingChunkEvent, FinalResponseEvent
|
|
98
|
+
|
|
99
|
+
async for event in agent.query_stream("Explain quantum computing", stream=True):
|
|
100
|
+
match event:
|
|
101
|
+
case ThinkingChunkEvent(content=delta):
|
|
102
|
+
print(delta, end="", flush=True) # thinking token delta
|
|
103
|
+
case TextChunkEvent(content=delta):
|
|
104
|
+
print(delta, end="", flush=True) # text token delta
|
|
105
|
+
case FinalResponseEvent(content=text):
|
|
106
|
+
print(f"\nDone: {text}")
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
**Key differences from default (`stream=False`):**
|
|
110
|
+
|
|
111
|
+
| | `stream=False` (default) | `stream=True` |
|
|
112
|
+
|---|---|---|
|
|
113
|
+
| First token latency | Waits for full response | Streams immediately |
|
|
114
|
+
| Event types | `TextEvent` (complete text) | `TextChunkEvent` per token + `TextEvent` (complete) |
|
|
115
|
+
| Thinking | `ThinkingEvent` (complete) | `ThinkingChunkEvent` per token + `ThinkingEvent` (complete) |
|
|
116
|
+
| Backward compat | — | Fully compatible, same tool execution flow |
|
|
117
|
+
|
|
118
|
+
**Enable extended thinking (GLM, DeepSeek):**
|
|
119
|
+
|
|
120
|
+
```python
|
|
121
|
+
from zai_adk.llm.openai.chat import ChatOpenAI
|
|
122
|
+
|
|
123
|
+
llm = ChatOpenAI(
|
|
124
|
+
model="glm-5.1",
|
|
125
|
+
thinking={"type": "enabled"}, # enable reasoning output
|
|
126
|
+
)
|
|
127
|
+
agent = Agent(llm=llm, tools=[])
|
|
128
|
+
|
|
129
|
+
async for event in agent.query_stream("Think carefully: 9.11 vs 9.8", stream=True):
|
|
130
|
+
match event:
|
|
131
|
+
case ThinkingChunkEvent(content=delta):
|
|
132
|
+
print(f"[think] {delta}", end="", flush=True)
|
|
133
|
+
case TextChunkEvent(content=delta):
|
|
134
|
+
print(delta, end="", flush=True)
|
|
135
|
+
```
|
|
136
|
+
|
|
92
137
|
### Sandboxed File Operations
|
|
93
138
|
|
|
94
139
|
```python
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
"""Quick manual test for true streaming vs pseudo-streaming.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
uv run python -m examples.test_streaming
|
|
5
|
+
|
|
6
|
+
Compare the output of stream=False vs stream=True:
|
|
7
|
+
- stream=False: you wait several seconds, then see the full text at once
|
|
8
|
+
- stream=True: you see text appear token-by-token in real time
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import asyncio
|
|
12
|
+
import time
|
|
13
|
+
|
|
14
|
+
from zai_adk import Agent
|
|
15
|
+
from zai_adk.llm.openai.chat import ChatOpenAI
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
async def test_stream_false():
|
|
19
|
+
"""Default behavior — collect full response then yield."""
|
|
20
|
+
llm = ChatOpenAI(
|
|
21
|
+
model="glm-4.7",
|
|
22
|
+
temperature=0.3,
|
|
23
|
+
max_completion_tokens=256,
|
|
24
|
+
)
|
|
25
|
+
agent = Agent(llm=llm, tools=[])
|
|
26
|
+
|
|
27
|
+
print("=" * 60)
|
|
28
|
+
print("stream=False (pseudo-streaming)")
|
|
29
|
+
print("=" * 60)
|
|
30
|
+
|
|
31
|
+
start = time.time()
|
|
32
|
+
async for event in agent.query_stream("用一句话介绍什么是量子计算"):
|
|
33
|
+
if hasattr(event, "content") and event.content:
|
|
34
|
+
elapsed = time.time() - start
|
|
35
|
+
print(f" [{elapsed:.2f}s] {type(event).__name__}: {event.content[:80]}")
|
|
36
|
+
print()
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
async def test_stream_true():
|
|
40
|
+
"""True streaming — token by token."""
|
|
41
|
+
llm = ChatOpenAI(
|
|
42
|
+
model="glm-4.7",
|
|
43
|
+
temperature=0.3,
|
|
44
|
+
max_completion_tokens=256,
|
|
45
|
+
)
|
|
46
|
+
agent = Agent(llm=llm, tools=[])
|
|
47
|
+
|
|
48
|
+
print("=" * 60)
|
|
49
|
+
print("stream=True (true streaming)")
|
|
50
|
+
print("=" * 60)
|
|
51
|
+
|
|
52
|
+
start = time.time()
|
|
53
|
+
full_text = []
|
|
54
|
+
async for event in agent.query_stream("用一句话介绍什么是量子计算", stream=True):
|
|
55
|
+
match event:
|
|
56
|
+
case _ if hasattr(event, "content") and isinstance(event.content, str) and event.content:
|
|
57
|
+
elapsed = time.time() - start
|
|
58
|
+
type_name = type(event).__name__
|
|
59
|
+
if type_name == "TextChunkEvent":
|
|
60
|
+
print(event.content, end="", flush=True)
|
|
61
|
+
full_text.append(event.content)
|
|
62
|
+
else:
|
|
63
|
+
print(f"\n [{elapsed:.2f}s] {type_name}: {event.content[:80]}")
|
|
64
|
+
print(f"\n Full text length: {len(''.join(full_text))} chars")
|
|
65
|
+
print()
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
async def test_stream_with_tools():
|
|
69
|
+
"""True streaming with tool calls."""
|
|
70
|
+
from zai_adk.tools.builtin import bash
|
|
71
|
+
|
|
72
|
+
llm = ChatOpenAI(
|
|
73
|
+
model="glm-4.7",
|
|
74
|
+
temperature=0.3,
|
|
75
|
+
max_completion_tokens=256,
|
|
76
|
+
)
|
|
77
|
+
agent = Agent(llm=llm, tools=[bash])
|
|
78
|
+
|
|
79
|
+
print("=" * 60)
|
|
80
|
+
print("stream=True with tool calls")
|
|
81
|
+
print("=" * 60)
|
|
82
|
+
|
|
83
|
+
start = time.time()
|
|
84
|
+
async for event in agent.query_stream("列出当前目录下的 Python 文件", stream=True):
|
|
85
|
+
match event:
|
|
86
|
+
case _ if hasattr(event, "content") and isinstance(event.content, str) and event.content:
|
|
87
|
+
type_name = type(event).__name__
|
|
88
|
+
if type_name == "TextChunkEvent":
|
|
89
|
+
print(event.content, end="", flush=True)
|
|
90
|
+
else:
|
|
91
|
+
elapsed = time.time() - start
|
|
92
|
+
print(f"\n [{elapsed:.2f}s] {type_name}: {event.content[:80]}")
|
|
93
|
+
print("\n")
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
if __name__ == "__main__":
|
|
97
|
+
import os
|
|
98
|
+
|
|
99
|
+
if not os.getenv("OPENAI_API_KEY") and not os.getenv("OPENAI_BASE_URL"):
|
|
100
|
+
print("Please set OPENAI_API_KEY or OPENAI_BASE_URL in .env")
|
|
101
|
+
exit(1)
|
|
102
|
+
|
|
103
|
+
print("\n>>> Test 1: Pseudo-streaming (default)")
|
|
104
|
+
asyncio.run(test_stream_false())
|
|
105
|
+
|
|
106
|
+
print("\n>>> Test 2: True streaming")
|
|
107
|
+
asyncio.run(test_stream_true())
|
|
108
|
+
|
|
109
|
+
print("\n>>> Test 3: True streaming with tools")
|
|
110
|
+
asyncio.run(test_stream_with_tools())
|
|
@@ -0,0 +1,450 @@
|
|
|
1
|
+
"""
|
|
2
|
+
True streaming real API test — covers text, tools, and skills.
|
|
3
|
+
|
|
4
|
+
Usage:
|
|
5
|
+
# Edit .env with your API credentials, then:
|
|
6
|
+
uv run python -m examples.test_streaming_real
|
|
7
|
+
|
|
8
|
+
# Or set env vars directly:
|
|
9
|
+
OPENAI_API_KEY=your-key OPENAI_BASE_URL=https://... uv run python -m examples.test_streaming_real
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import asyncio
|
|
13
|
+
import os
|
|
14
|
+
import sys
|
|
15
|
+
import time
|
|
16
|
+
from pathlib import Path
|
|
17
|
+
|
|
18
|
+
from dotenv import load_dotenv
|
|
19
|
+
|
|
20
|
+
load_dotenv()
|
|
21
|
+
|
|
22
|
+
from zai_adk import Agent
|
|
23
|
+
from zai_adk.agent.events import (
|
|
24
|
+
FinalResponseEvent,
|
|
25
|
+
TextChunkEvent,
|
|
26
|
+
TextEvent,
|
|
27
|
+
ThinkingChunkEvent,
|
|
28
|
+
ToolCallEvent,
|
|
29
|
+
ToolResultEvent,
|
|
30
|
+
)
|
|
31
|
+
from zai_adk.llm.openai.chat import ChatOpenAI
|
|
32
|
+
from zai_adk.sandbox import SandboxConfig
|
|
33
|
+
from zai_adk.tools.builtin import bash, fs_read, fs_write, fs_glob, fs_grep
|
|
34
|
+
from zai_adk.tools.decorator import tool
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
# ─── Custom tools ───────────────────────────────────────────────────────────
|
|
38
|
+
|
|
39
|
+
@tool("Calculate the result of a math expression")
|
|
40
|
+
async def calculator(expression: str) -> str:
|
|
41
|
+
"""Safely evaluate a math expression."""
|
|
42
|
+
import ast
|
|
43
|
+
import operator
|
|
44
|
+
|
|
45
|
+
ops = {
|
|
46
|
+
ast.Add: operator.add, ast.Sub: operator.sub,
|
|
47
|
+
ast.Mult: operator.mul, ast.Div: operator.truediv,
|
|
48
|
+
ast.Pow: operator.pow, ast.USub: operator.neg,
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
def _eval(node):
|
|
52
|
+
if isinstance(node, ast.Constant):
|
|
53
|
+
return node.value
|
|
54
|
+
if isinstance(node, ast.UnaryOp):
|
|
55
|
+
return ops[type(node.op)](_eval(node.operand))
|
|
56
|
+
if isinstance(node, ast.BinOp):
|
|
57
|
+
return ops[type(node.op)](_eval(node.left), _eval(node.right))
|
|
58
|
+
raise ValueError(f"Unsupported: {type(node).__name__}")
|
|
59
|
+
|
|
60
|
+
try:
|
|
61
|
+
result = _eval(ast.parse(expression, mode="eval").body)
|
|
62
|
+
return str(result)
|
|
63
|
+
except Exception as e:
|
|
64
|
+
return f"Error: {e}"
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@tool("Get the current time")
|
|
68
|
+
async def get_time() -> str:
|
|
69
|
+
from datetime import datetime
|
|
70
|
+
return datetime.now().strftime("%Y-%m-%d %H:%M:%S")
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# ─── LLM setup ─────────────────────────────────────────────────────────────
|
|
74
|
+
|
|
75
|
+
def create_llm(thinking: bool = False):
|
|
76
|
+
api_key = os.getenv("OPENAI_API_KEY")
|
|
77
|
+
base_url = os.getenv("OPENAI_BASE_URL")
|
|
78
|
+
model = os.getenv("OPENAI_MODEL", "glm-4.7")
|
|
79
|
+
|
|
80
|
+
if not api_key and not base_url:
|
|
81
|
+
print("ERROR: Set OPENAI_API_KEY or OPENAI_BASE_URL in .env or environment")
|
|
82
|
+
sys.exit(1)
|
|
83
|
+
|
|
84
|
+
return ChatOpenAI(
|
|
85
|
+
model=model,
|
|
86
|
+
api_key=api_key,
|
|
87
|
+
base_url=base_url,
|
|
88
|
+
temperature=0.3,
|
|
89
|
+
max_completion_tokens=2048,
|
|
90
|
+
thinking={"type": "enabled"} if thinking else None,
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# ─── Color helpers ──────────────────────────────────────────────────────────
|
|
95
|
+
|
|
96
|
+
class C:
|
|
97
|
+
"""ANSI colors for terminal output."""
|
|
98
|
+
BOLD = "\033[1m"
|
|
99
|
+
DIM = "\033[2m"
|
|
100
|
+
CYAN = "\033[36m"
|
|
101
|
+
GREEN = "\033[32m"
|
|
102
|
+
YELLOW = "\033[33m"
|
|
103
|
+
BLUE = "\033[34m"
|
|
104
|
+
MAGENTA = "\033[35m"
|
|
105
|
+
RED = "\033[31m"
|
|
106
|
+
RESET = "\033[0m"
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
def header(title: str):
|
|
110
|
+
print(f"\n{'=' * 70}")
|
|
111
|
+
print(f" {C.BOLD}{C.CYAN}{title}{C.RESET}")
|
|
112
|
+
print(f"{'=' * 70}\n")
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
# ─── Test cases ─────────────────────────────────────────────────────────────
|
|
116
|
+
|
|
117
|
+
async def test_1_text_only_streaming():
|
|
118
|
+
"""Test 1: Pure text streaming — compare stream=False vs stream=True."""
|
|
119
|
+
header("Test 1: Text-only streaming comparison")
|
|
120
|
+
tools = []
|
|
121
|
+
prompt = "用三句话解释什么是大语言模型(LLM)。"
|
|
122
|
+
|
|
123
|
+
# --- stream=False (pseudo-streaming) ---
|
|
124
|
+
agent = Agent(llm=create_llm(), tools=tools)
|
|
125
|
+
print(f" {C.DIM}[stream=False] Waiting for full response...{C.RESET}")
|
|
126
|
+
start = time.time()
|
|
127
|
+
first_token_time = None
|
|
128
|
+
async for event in agent.query_stream(prompt, stream=False):
|
|
129
|
+
if isinstance(event, TextEvent):
|
|
130
|
+
first_token_time = time.time() - start
|
|
131
|
+
print(f" {C.GREEN}TextEvent (full): {event.content[:60]}...{C.RESET}")
|
|
132
|
+
elif isinstance(event, FinalResponseEvent):
|
|
133
|
+
total_time = time.time() - start
|
|
134
|
+
print(f" {C.DIM}First token: {first_token_time:.2f}s | Total: {total_time:.2f}s{C.RESET}")
|
|
135
|
+
|
|
136
|
+
# --- stream=True (true streaming) ---
|
|
137
|
+
agent = Agent(llm=create_llm(), tools=tools)
|
|
138
|
+
print(f"\n {C.DIM}[stream=True] Streaming token by token...{C.RESET}")
|
|
139
|
+
start = time.time()
|
|
140
|
+
first_chunk_time = None
|
|
141
|
+
chunk_count = 0
|
|
142
|
+
full_text = []
|
|
143
|
+
print(f" {C.YELLOW}", end="", flush=True)
|
|
144
|
+
async for event in agent.query_stream(prompt, stream=True):
|
|
145
|
+
if isinstance(event, TextChunkEvent):
|
|
146
|
+
if first_chunk_time is None:
|
|
147
|
+
first_chunk_time = time.time() - start
|
|
148
|
+
chunk_count += 1
|
|
149
|
+
full_text.append(event.content)
|
|
150
|
+
print(event.content, end="", flush=True)
|
|
151
|
+
elif isinstance(event, TextEvent):
|
|
152
|
+
pass # complete text, already printed via chunks
|
|
153
|
+
elif isinstance(event, FinalResponseEvent):
|
|
154
|
+
total_time = time.time() - start
|
|
155
|
+
print(f"{C.RESET}")
|
|
156
|
+
print(f" {C.DIM}Chunks: {chunk_count} | First token: {first_chunk_time:.2f}s | Total: {total_time:.2f}s{C.RESET}")
|
|
157
|
+
print(f" {C.DIM}Full text: {''.join(full_text)[:80]}...{C.RESET}")
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
async def test_2_builtin_tools():
|
|
161
|
+
"""Test 2: Streaming with built-in tools (bash, fs_glob, fs_read)."""
|
|
162
|
+
header("Test 2: Streaming with built-in tools")
|
|
163
|
+
workspace = str(Path(__file__).parent.parent) # project root
|
|
164
|
+
tools = [bash, fs_glob, fs_read]
|
|
165
|
+
prompt = f"列出 {workspace}/zai_adk/llm/ 目录下的所有 Python 文件,并读取 base.py 的前 5 行。"
|
|
166
|
+
|
|
167
|
+
agent = Agent(
|
|
168
|
+
llm=create_llm(),
|
|
169
|
+
tools=tools,
|
|
170
|
+
sandbox=SandboxConfig(kind="local", work_dir=workspace),
|
|
171
|
+
)
|
|
172
|
+
|
|
173
|
+
print(f" {C.DIM}Prompt: {prompt}{C.RESET}\n")
|
|
174
|
+
start = time.time()
|
|
175
|
+
async for event in agent.query_stream(prompt, stream=True):
|
|
176
|
+
match event:
|
|
177
|
+
case TextChunkEvent(content=delta):
|
|
178
|
+
print(f"{C.YELLOW}{delta}{C.RESET}", end="", flush=True)
|
|
179
|
+
case TextEvent():
|
|
180
|
+
pass
|
|
181
|
+
case ToolCallEvent(tool=name, args=args):
|
|
182
|
+
print(f"\n {C.BLUE}>> Tool call: {name}({args}){C.RESET}")
|
|
183
|
+
case ToolResultEvent(tool=name, result=result):
|
|
184
|
+
preview = result[:150].replace("\n", "\\n")
|
|
185
|
+
print(f" {C.GREEN}<< {name}: {preview}...{C.RESET}")
|
|
186
|
+
case FinalResponseEvent():
|
|
187
|
+
elapsed = time.time() - start
|
|
188
|
+
print(f"\n {C.DIM}Completed in {elapsed:.2f}s{C.RESET}")
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
async def test_3_custom_tools():
|
|
192
|
+
"""Test 3: Streaming with custom tools (calculator, get_time)."""
|
|
193
|
+
header("Test 3: Streaming with custom tools")
|
|
194
|
+
tools = [calculator, get_time]
|
|
195
|
+
prompt = "现在几点了?然后帮我算一下 (1234 + 5678) * 3 的结果。"
|
|
196
|
+
|
|
197
|
+
agent = Agent(llm=create_llm(), tools=tools)
|
|
198
|
+
|
|
199
|
+
print(f" {C.DIM}Prompt: {prompt}{C.RESET}\n")
|
|
200
|
+
async for event in agent.query_stream(prompt, stream=True):
|
|
201
|
+
match event:
|
|
202
|
+
case TextChunkEvent(content=delta):
|
|
203
|
+
print(f"{C.YELLOW}{delta}{C.RESET}", end="", flush=True)
|
|
204
|
+
case TextEvent():
|
|
205
|
+
pass
|
|
206
|
+
case ToolCallEvent(tool=name, args=args):
|
|
207
|
+
print(f"\n {C.BLUE}>> Tool call: {name}({args}){C.RESET}")
|
|
208
|
+
case ToolResultEvent(tool=name, result=result):
|
|
209
|
+
print(f" {C.GREEN}<< {name}: {result}{C.RESET}")
|
|
210
|
+
case FinalResponseEvent():
|
|
211
|
+
print()
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
async def test_4_multi_tool_workflow():
|
|
215
|
+
"""Test 4: Complex workflow — write file, read it back, search content."""
|
|
216
|
+
header("Test 4: Multi-tool workflow (write → read → grep)")
|
|
217
|
+
workspace = str(Path(__file__).parent)
|
|
218
|
+
tools = [fs_write, fs_read, fs_grep, bash]
|
|
219
|
+
|
|
220
|
+
agent = Agent(
|
|
221
|
+
llm=create_llm(),
|
|
222
|
+
tools=tools,
|
|
223
|
+
sandbox=SandboxConfig(kind="local", work_dir=workspace),
|
|
224
|
+
)
|
|
225
|
+
|
|
226
|
+
prompt = (
|
|
227
|
+
"请完成以下任务:\n"
|
|
228
|
+
"1. 在当前目录创建一个文件 test_stream_data.txt,内容是 3 种编程语言的简介(每行一个)\n"
|
|
229
|
+
"2. 读取这个文件确认内容正确\n"
|
|
230
|
+
"3. 搜索文件中包含 Python 的行"
|
|
231
|
+
)
|
|
232
|
+
|
|
233
|
+
print(f" {C.DIM}Prompt: {prompt[:80]}...{C.RESET}\n")
|
|
234
|
+
tool_calls = 0
|
|
235
|
+
async for event in agent.query_stream(prompt, stream=True):
|
|
236
|
+
match event:
|
|
237
|
+
case TextChunkEvent(content=delta):
|
|
238
|
+
print(f"{C.YELLOW}{delta}{C.RESET}", end="", flush=True)
|
|
239
|
+
case TextEvent():
|
|
240
|
+
pass
|
|
241
|
+
case ToolCallEvent(tool=name, args=args):
|
|
242
|
+
tool_calls += 1
|
|
243
|
+
args_str = str(args)
|
|
244
|
+
if len(args_str) > 80:
|
|
245
|
+
args_str = args_str[:77] + "..."
|
|
246
|
+
print(f"\n {C.BLUE}[{tool_calls}] >> {name}({args_str}){C.RESET}")
|
|
247
|
+
case ToolResultEvent(tool=name, result=result):
|
|
248
|
+
preview = result[:120].replace("\n", "\\n")
|
|
249
|
+
print(f" {C.GREEN} << {name}: {preview}{C.RESET}")
|
|
250
|
+
case FinalResponseEvent():
|
|
251
|
+
print(f"\n {C.DIM}Total tool calls: {tool_calls}{C.RESET}")
|
|
252
|
+
|
|
253
|
+
# Cleanup
|
|
254
|
+
data_file = Path(workspace) / "test_stream_data.txt"
|
|
255
|
+
if data_file.exists():
|
|
256
|
+
data_file.unlink()
|
|
257
|
+
print(f" {C.DIM}Cleaned up test_stream_data.txt{C.RESET}")
|
|
258
|
+
|
|
259
|
+
|
|
260
|
+
async def test_5_skills():
|
|
261
|
+
"""Test 5: Streaming with skills — create a skill and use it."""
|
|
262
|
+
header("Test 5: Streaming with skills")
|
|
263
|
+
|
|
264
|
+
# Create a temporary skills directory with a skill
|
|
265
|
+
workspace = str(Path(__file__).parent)
|
|
266
|
+
skills_dir = Path(workspace) / "_test_skills"
|
|
267
|
+
skills_dir.mkdir(exist_ok=True)
|
|
268
|
+
|
|
269
|
+
# Create a code-review skill
|
|
270
|
+
review_skill_dir = skills_dir / "code-review"
|
|
271
|
+
review_skill_dir.mkdir(exist_ok=True)
|
|
272
|
+
(review_skill_dir / "SKILL.md").write_text("""\
|
|
273
|
+
---
|
|
274
|
+
name: code-review
|
|
275
|
+
description: Review code for bugs, security issues, and best practices
|
|
276
|
+
---
|
|
277
|
+
|
|
278
|
+
# Code Review Skill
|
|
279
|
+
|
|
280
|
+
When reviewing code:
|
|
281
|
+
1. Check for security vulnerabilities (SQL injection, XSS, etc.)
|
|
282
|
+
2. Check for error handling issues
|
|
283
|
+
3. Check for performance problems
|
|
284
|
+
4. Check for code style and readability
|
|
285
|
+
5. Provide specific suggestions for improvement
|
|
286
|
+
""")
|
|
287
|
+
|
|
288
|
+
# Create a git-commit skill
|
|
289
|
+
commit_skill_dir = skills_dir / "git-commit"
|
|
290
|
+
commit_skill_dir.mkdir(exist_ok=True)
|
|
291
|
+
(commit_skill_dir / "SKILL.md").write_text("""\
|
|
292
|
+
---
|
|
293
|
+
name: git-commit
|
|
294
|
+
description: Generate conventional commit messages following project conventions
|
|
295
|
+
---
|
|
296
|
+
|
|
297
|
+
# Git Commit Skill
|
|
298
|
+
|
|
299
|
+
Generate commit messages following the Conventional Commits specification:
|
|
300
|
+
- feat: New features
|
|
301
|
+
- fix: Bug fixes
|
|
302
|
+
- docs: Documentation changes
|
|
303
|
+
- refactor: Code refactoring
|
|
304
|
+
- test: Test additions/changes
|
|
305
|
+
- chore: Build/tooling changes
|
|
306
|
+
""")
|
|
307
|
+
|
|
308
|
+
tools = [bash, fs_read]
|
|
309
|
+
agent = Agent(
|
|
310
|
+
llm=create_llm(),
|
|
311
|
+
tools=tools,
|
|
312
|
+
skills_dir=str(skills_dir),
|
|
313
|
+
sandbox=SandboxConfig(kind="local", work_dir=str(Path(__file__).parent.parent)),
|
|
314
|
+
)
|
|
315
|
+
|
|
316
|
+
# Must call async_initialize to load skills
|
|
317
|
+
await agent.async_initialize()
|
|
318
|
+
|
|
319
|
+
print(f" {C.DIM}Loaded skills: {[s.name for s in agent._skills_manager.list()]}{C.RESET}")
|
|
320
|
+
print(f" {C.DIM}System prompt includes skills metadata: {'skills' in (agent.system_prompt or '').lower()}{C.RESET}\n")
|
|
321
|
+
|
|
322
|
+
prompt = "请列出所有可用的 skills,然后用 code-review skill 来审查以下代码的安全性: def query(user_input): return db.execute(f'SELECT * FROM users WHERE name={user_input}')"
|
|
323
|
+
|
|
324
|
+
async for event in agent.query_stream(prompt, stream=True):
|
|
325
|
+
match event:
|
|
326
|
+
case TextChunkEvent(content=delta):
|
|
327
|
+
print(f"{C.YELLOW}{delta}{C.RESET}", end="", flush=True)
|
|
328
|
+
case TextEvent():
|
|
329
|
+
pass
|
|
330
|
+
case ToolCallEvent(tool=name, args=args):
|
|
331
|
+
args_str = str(args)
|
|
332
|
+
if len(args_str) > 80:
|
|
333
|
+
args_str = args_str[:77] + "..."
|
|
334
|
+
print(f"\n {C.BLUE}>> {name}({args_str}){C.RESET}")
|
|
335
|
+
case ToolResultEvent(tool=name, result=result):
|
|
336
|
+
preview = result[:120].replace("\n", "\\n")
|
|
337
|
+
print(f" {C.GREEN} << {name}: {preview}{C.RESET}")
|
|
338
|
+
case FinalResponseEvent():
|
|
339
|
+
print()
|
|
340
|
+
|
|
341
|
+
# Cleanup
|
|
342
|
+
import shutil
|
|
343
|
+
if skills_dir.exists():
|
|
344
|
+
shutil.rmtree(skills_dir)
|
|
345
|
+
print(f" {C.DIM}Cleaned up _test_skills/{C.RESET}")
|
|
346
|
+
|
|
347
|
+
|
|
348
|
+
async def test_6_thinking_streaming():
|
|
349
|
+
"""Test 6: Thinking content streaming (if model supports extended thinking)."""
|
|
350
|
+
header("Test 6: Thinking streaming")
|
|
351
|
+
tools = []
|
|
352
|
+
prompt = "9.11 和 9.8 哪个更大?请仔细思考后再回答。"
|
|
353
|
+
|
|
354
|
+
agent = Agent(llm=create_llm(thinking=True), tools=tools)
|
|
355
|
+
|
|
356
|
+
print(f" {C.DIM}Prompt: {prompt}{C.RESET}")
|
|
357
|
+
thinking_chunks = 0
|
|
358
|
+
text_chunks = 0
|
|
359
|
+
thinking_parts: list[str] = []
|
|
360
|
+
in_thinking = False
|
|
361
|
+
in_response = False
|
|
362
|
+
|
|
363
|
+
async for event in agent.query_stream(prompt, stream=True):
|
|
364
|
+
match event:
|
|
365
|
+
case ThinkingChunkEvent(content=delta):
|
|
366
|
+
thinking_chunks += 1
|
|
367
|
+
thinking_parts.append(delta)
|
|
368
|
+
if not in_thinking:
|
|
369
|
+
in_thinking = True
|
|
370
|
+
print(f"\n {C.BOLD}{C.MAGENTA}[Thinking]{C.RESET} ", end="", flush=True)
|
|
371
|
+
print(f"{C.MAGENTA}{delta}{C.RESET}", end="", flush=True)
|
|
372
|
+
case TextChunkEvent(content=delta):
|
|
373
|
+
if in_thinking:
|
|
374
|
+
in_thinking = False
|
|
375
|
+
print() # end thinking line
|
|
376
|
+
print(f"\n {C.BOLD}{C.YELLOW}[Response]{C.RESET} ", end="", flush=True)
|
|
377
|
+
in_response = True
|
|
378
|
+
text_chunks += 1
|
|
379
|
+
print(f"{C.YELLOW}{delta}{C.RESET}", end="", flush=True)
|
|
380
|
+
case TextEvent():
|
|
381
|
+
pass
|
|
382
|
+
case FinalResponseEvent():
|
|
383
|
+
if in_response or in_thinking:
|
|
384
|
+
print()
|
|
385
|
+
thinking_text = "".join(thinking_parts)
|
|
386
|
+
print(f"\n {C.DIM}{'─' * 50}{C.RESET}")
|
|
387
|
+
print(f" {C.BOLD}Stats:{C.RESET}")
|
|
388
|
+
print(f" Thinking: {C.MAGENTA}{thinking_chunks} chunks{C.RESET} ({len(thinking_text)} chars)")
|
|
389
|
+
print(f" Text: {C.YELLOW}{text_chunks} chunks{C.RESET}")
|
|
390
|
+
print(f" {C.DIM}{'─' * 50}{C.RESET}")
|
|
391
|
+
|
|
392
|
+
|
|
393
|
+
# ─── Main ───────────────────────────────────────────────────────────────────
|
|
394
|
+
|
|
395
|
+
TESTS = [
|
|
396
|
+
("1", "Text-only streaming comparison", test_1_text_only_streaming),
|
|
397
|
+
("2", "Built-in tools (bash, fs_glob, fs_read)", test_2_builtin_tools),
|
|
398
|
+
("3", "Custom tools (calculator, get_time)", test_3_custom_tools),
|
|
399
|
+
("4", "Multi-tool workflow (write→read→grep)", test_4_multi_tool_workflow),
|
|
400
|
+
("5", "Skills (code-review, git-commit)", test_5_skills),
|
|
401
|
+
("6", "Thinking streaming", test_6_thinking_streaming),
|
|
402
|
+
]
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
async def run_all():
|
|
406
|
+
for _, desc, test_fn in TESTS:
|
|
407
|
+
try:
|
|
408
|
+
await test_fn()
|
|
409
|
+
except Exception as e:
|
|
410
|
+
print(f"\n {C.RED}FAILED: {e}{C.RESET}")
|
|
411
|
+
import traceback
|
|
412
|
+
traceback.print_exc()
|
|
413
|
+
|
|
414
|
+
print(f"\n{'=' * 70}")
|
|
415
|
+
print(f" {C.BOLD}{C.GREEN}All tests completed.{C.RESET}")
|
|
416
|
+
print(f"{'=' * 70}\n")
|
|
417
|
+
|
|
418
|
+
|
|
419
|
+
async def run_one(choice: str):
|
|
420
|
+
for key, desc, test_fn in TESTS:
|
|
421
|
+
if key == choice:
|
|
422
|
+
print(f"\n Running: {C.BOLD}{desc}{C.RESET}\n")
|
|
423
|
+
try:
|
|
424
|
+
await test_fn()
|
|
425
|
+
except Exception as e:
|
|
426
|
+
print(f"\n {C.RED}FAILED: {e}{C.RESET}")
|
|
427
|
+
import traceback
|
|
428
|
+
traceback.print_exc()
|
|
429
|
+
return
|
|
430
|
+
print(f" {C.RED}Unknown test: {choice}{C.RESET}")
|
|
431
|
+
|
|
432
|
+
|
|
433
|
+
if __name__ == "__main__":
|
|
434
|
+
print(f"\n{C.BOLD}zai-adk True Streaming Test Suite{C.RESET}")
|
|
435
|
+
print(f" Model: {os.getenv('OPENAI_MODEL', 'glm-4.7')}")
|
|
436
|
+
print(f" Base URL: {os.getenv('OPENAI_BASE_URL', '(default)')}")
|
|
437
|
+
|
|
438
|
+
if len(sys.argv) > 1:
|
|
439
|
+
choice = sys.argv[1]
|
|
440
|
+
if choice in ("all", "a"):
|
|
441
|
+
asyncio.run(run_all())
|
|
442
|
+
else:
|
|
443
|
+
asyncio.run(run_one(choice))
|
|
444
|
+
else:
|
|
445
|
+
print(f"\n Usage: python -m examples.test_streaming_real [test_number|all]")
|
|
446
|
+
print(f"\n Available tests:")
|
|
447
|
+
for key, desc, _ in TESTS:
|
|
448
|
+
print(f" {C.BOLD}{key}{C.RESET} {desc}")
|
|
449
|
+
print(f" {C.BOLD}all{C.RESET} Run all tests")
|
|
450
|
+
print()
|