atomic-agents 2.6.0__tar.gz → 2.7.3__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.
- atomic_agents-2.7.3/.claude/skills/release/SKILL.md +104 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/.gitignore +2 -0
- atomic_agents-2.7.3/.pre-commit-config.yaml +20 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/PKG-INFO +1 -1
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/atomic_agents/context/chat_history.py +72 -44
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/atomic_agents/utils/token_counter.py +5 -2
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/tests/connectors/mcp/test_mcp_factory.py +1 -3
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/tests/connectors/mcp/test_schema_transformer.py +3 -9
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/tests/context/test_chat_history.py +327 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/tests/utils/test_token_counter.py +63 -25
- atomic_agents-2.7.3/atomic-examples/nested-multimodal/README.md +68 -0
- atomic_agents-2.7.3/atomic-examples/nested-multimodal/nested_multimodal/main.py +179 -0
- atomic_agents-2.7.3/atomic-examples/nested-multimodal/pyproject.toml +25 -0
- atomic_agents-2.7.3/atomic-examples/nested-multimodal/test_images/nutrition_label_1.png +0 -0
- atomic_agents-2.7.3/atomic-forge/tools/bocha_search/README.md +85 -0
- atomic_agents-2.7.3/atomic-forge/tools/bocha_search/pyproject.toml +34 -0
- atomic_agents-2.7.3/atomic-forge/tools/bocha_search/requirements.txt +3 -0
- atomic_agents-2.7.3/atomic-forge/tools/bocha_search/tests/test_bocha_search.py +254 -0
- atomic_agents-2.7.3/atomic-forge/tools/bocha_search/tool/bocha_search.py +196 -0
- atomic_agents-2.7.3/atomic-forge/tools/youtube_transcript_scraper/.coveragerc +8 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/guides/tools.md +1 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/pyproject.toml +2 -1
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/uv.lock +61 -1
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/.assets/architecture_highlevel_overview.png +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/.assets/atomic-cli-tool-menu.png +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/.assets/atomic-cli.png +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/.assets/docs.png +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/.assets/logo.png +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/.assets/video-thumbnail-1.png +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/.assets/video-thumbnail-2.png +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/.assets/video-thumbnail.png +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/.assets/what_is_sent_in_prompt.png +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/.claude-plugin/marketplace.json +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/.coveragerc +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/.flake8 +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/.github/funding.yml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/.github/workflows/code-quality.yml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/.github/workflows/docs.yml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/LICENSE +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/UPGRADE_DOC.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/agents.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/LICENSE +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/MANIFEST.in +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/atomic_agents/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/atomic_agents/agents/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/atomic_agents/agents/atomic_agent.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/atomic_agents/base/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/atomic_agents/base/base_io_schema.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/atomic_agents/base/base_prompt.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/atomic_agents/base/base_resource.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/atomic_agents/base/base_tool.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/atomic_agents/connectors/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/atomic_agents/connectors/mcp/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/atomic_agents/connectors/mcp/mcp_definition_service.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/atomic_agents/connectors/mcp/mcp_factory.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/atomic_agents/connectors/mcp/schema_transformer.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/atomic_agents/context/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/atomic_agents/context/system_prompt_generator.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/atomic_agents/utils/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/atomic_agents/utils/format_tool_message.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/tests/agents/test_atomic_agent.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/tests/base/test_base_tool.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/tests/connectors/mcp/test_mcp_definition_service.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/tests/context/test_system_prompt_generator.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/tests/files/audio_sample.mp3 +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/tests/files/image_sample.jpg +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/tests/files/pdf_sample.pdf +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/tests/utils/test_format_tool_message.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-assembler/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-assembler/atomic_assembler/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-assembler/atomic_assembler/app.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-assembler/atomic_assembler/color_utils.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-assembler/atomic_assembler/constants.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-assembler/atomic_assembler/main.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-assembler/atomic_assembler/screens/atomic_tool_explorer.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-assembler/atomic_assembler/screens/file_explorer.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-assembler/atomic_assembler/screens/main_menu.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-assembler/atomic_assembler/screens/tool_info_screen.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-assembler/atomic_assembler/utils.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-assembler/atomic_assembler/widgets/confirmation_modal.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-assembler/atomic_assembler/widgets/generic_list.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-assembler/atomic_assembler/widgets/gradient_title.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-assembler/atomic_assembler/widgets/icon_selection_list.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-assembler/atomic_assembler/widgets/menu.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-assembler/atomic_assembler/widgets/new_item_input.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-assembler/atomic_assembler/widgets/tool_info_display.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/basic-multimodal/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/basic-multimodal/basic_multimodal/main.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/basic-multimodal/pyproject.toml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/basic-multimodal/test_images/nutrition_label_1.png +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/basic-multimodal/test_images/nutrition_label_2.jpg +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/basic-pdf-analysis/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/basic-pdf-analysis/basic_pdf_analysis/main.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/basic-pdf-analysis/pyproject.toml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/basic-pdf-analysis/test_media/pdf_sample.pdf +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/deep-research/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/deep-research/deep_research/agents/choice_agent.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/deep-research/deep_research/agents/qa_agent.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/deep-research/deep_research/agents/query_agent.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/deep-research/deep_research/config.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/deep-research/deep_research/context_providers.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/deep-research/deep_research/main.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/deep-research/deep_research/tools/searxng_search.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/deep-research/deep_research/tools/webpage_scraper.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/deep-research/mermaid.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/deep-research/pyproject.toml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/dspy-integration/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/dspy-integration/dspy_integration/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/dspy-integration/dspy_integration/bridge.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/dspy-integration/dspy_integration/domain/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/dspy-integration/dspy_integration/domain/datasets.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/dspy-integration/dspy_integration/domain/evaluation.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/dspy-integration/dspy_integration/domain/models.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/dspy-integration/dspy_integration/main.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/dspy-integration/dspy_integration/presentation/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/dspy-integration/dspy_integration/presentation/console.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/dspy-integration/dspy_integration/schemas.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/dspy-integration/dspy_integration/stages/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/dspy-integration/dspy_integration/stages/stage1_dspy.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/dspy-integration/dspy_integration/stages/stage2_atomic.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/dspy-integration/dspy_integration/stages/stage3_combined.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/dspy-integration/pyproject.toml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/fastapi-memory/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/fastapi-memory/fastapi_memory/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/fastapi-memory/fastapi_memory/client.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/fastapi-memory/fastapi_memory/lib/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/fastapi-memory/fastapi_memory/lib/agents/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/fastapi-memory/fastapi_memory/lib/agents/chat_agent.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/fastapi-memory/fastapi_memory/lib/config.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/fastapi-memory/fastapi_memory/lib/schemas.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/fastapi-memory/fastapi_memory/main.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/fastapi-memory/pyproject.toml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/fastapi-memory/test_api.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/hooks-example/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/hooks-example/hooks_example/main.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/hooks-example/pyproject.toml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-client/example_client/main.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-client/example_client/main_fastapi.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-client/example_client/main_http.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-client/example_client/main_sse.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-client/example_client/main_stdio.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-client/example_client/main_stdio_async.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-client/pyproject.toml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/demo_tools.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/example_mcp_server/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/example_mcp_server/interfaces/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/example_mcp_server/interfaces/prompt.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/example_mcp_server/interfaces/resource.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/example_mcp_server/interfaces/tool.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/example_mcp_server/prompts/sample_prompts.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/example_mcp_server/resources/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/example_mcp_server/resources/sample_resources.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/example_mcp_server/server.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/example_mcp_server/server_http.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/example_mcp_server/server_sse.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/example_mcp_server/server_stdio.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/example_mcp_server/services/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/example_mcp_server/services/prompt_service.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/example_mcp_server/services/resource_service.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/example_mcp_server/services/tool_service.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/example_mcp_server/tools/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/example_mcp_server/tools/add_numbers.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/example_mcp_server/tools/batch_operations.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/example_mcp_server/tools/divide_numbers.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/example_mcp_server/tools/multiply_numbers.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/example_mcp_server/tools/subtract_numbers.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/mcp-agent/example-mcp-server/pyproject.toml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/orchestration-agent/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/orchestration-agent/orchestration_agent/orchestrator.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/orchestration-agent/orchestration_agent/tools/calculator.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/orchestration-agent/orchestration_agent/tools/searxng_search.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/orchestration-agent/pyproject.toml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/progressive-disclosure/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/progressive-disclosure/progressive_disclosure/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/progressive-disclosure/progressive_disclosure/agents/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/progressive-disclosure/progressive_disclosure/agents/orchestrator_agent.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/progressive-disclosure/progressive_disclosure/agents/tool_finder_agent.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/progressive-disclosure/progressive_disclosure/main.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/progressive-disclosure/progressive_disclosure/registry/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/progressive-disclosure/progressive_disclosure/registry/tool_registry.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/progressive-disclosure/progressive_disclosure/tools/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/progressive-disclosure/progressive_disclosure/tools/search_tools.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/progressive-disclosure/pyproject.toml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/progressive-disclosure/servers/data_server/data_server/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/progressive-disclosure/servers/data_server/data_server/server.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/progressive-disclosure/servers/data_server/pyproject.toml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/progressive-disclosure/servers/math_server/math_server/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/progressive-disclosure/servers/math_server/math_server/server.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/progressive-disclosure/servers/math_server/pyproject.toml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/progressive-disclosure/servers/text_server/pyproject.toml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/progressive-disclosure/servers/text_server/text_server/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/progressive-disclosure/servers/text_server/text_server/server.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/quickstart/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/quickstart/pyproject.toml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/quickstart/quickstart/1_0_basic_chatbot.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/quickstart/quickstart/1_1_basic_chatbot_streaming.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/quickstart/quickstart/1_2_basic_chatbot_async_streaming.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/quickstart/quickstart/2_basic_custom_chatbot.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/quickstart/quickstart/3_0_basic_custom_chatbot_with_custom_schema.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/quickstart/quickstart/3_1_basic_custom_chatbot_with_custom_schema_streaming.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/quickstart/quickstart/4_basic_chatbot_different_providers.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/quickstart/quickstart/5_custom_system_role_for_reasoning_models.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/quickstart/quickstart/6_0_asynchronous_processing.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/quickstart/quickstart/6_1_asynchronous_processing_streaming.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/rag-chatbot/.gitignore +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/rag-chatbot/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/rag-chatbot/pyproject.toml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/rag-chatbot/rag_chatbot/agents/qa_agent.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/rag-chatbot/rag_chatbot/agents/query_agent.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/rag-chatbot/rag_chatbot/config.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/rag-chatbot/rag_chatbot/context_providers.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/rag-chatbot/rag_chatbot/main.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/rag-chatbot/rag_chatbot/services/__init__.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/rag-chatbot/rag_chatbot/services/base.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/rag-chatbot/rag_chatbot/services/chroma_db.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/rag-chatbot/rag_chatbot/services/factory.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/rag-chatbot/rag_chatbot/services/qdrant_db.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/web-search-agent/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/web-search-agent/pyproject.toml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/web-search-agent/web_search_agent/agents/query_agent.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/web-search-agent/web_search_agent/agents/question_answering_agent.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/web-search-agent/web_search_agent/main.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/web-search-agent/web_search_agent/tools/searxng_search.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/youtube-summarizer/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/youtube-summarizer/pyproject.toml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/youtube-summarizer/youtube_summarizer/agent.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/youtube-summarizer/youtube_summarizer/main.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/youtube-summarizer/youtube_summarizer/tools/youtube_transcript_scraper.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/youtube-to-recipe/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/youtube-to-recipe/pyproject.toml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/youtube-to-recipe/youtube_to_recipe/agent.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/youtube-to-recipe/youtube_to_recipe/main.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-examples/youtube-to-recipe/youtube_to_recipe/tools/youtube_transcript_scraper.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/guides/tool_structure.md +0 -0
- {atomic_agents-2.6.0/atomic-forge/tools/calculator → atomic_agents-2.7.3/atomic-forge/tools/bocha_search}/.coveragerc +0 -0
- {atomic_agents-2.6.0/atomic-forge/tools/searxng_search → atomic_agents-2.7.3/atomic-forge/tools/calculator}/.coveragerc +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/calculator/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/calculator/pyproject.toml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/calculator/requirements.txt +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/calculator/tests/test_calculator.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/calculator/tool/calculator.py +0 -0
- {atomic_agents-2.6.0/atomic-forge/tools/tavily_search → atomic_agents-2.7.3/atomic-forge/tools/searxng_search}/.coveragerc +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/searxng_search/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/searxng_search/pyproject.toml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/searxng_search/pytest.ini +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/searxng_search/requirements.txt +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/searxng_search/tests/test_searxng_search.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/searxng_search/tool/searxng_search.py +0 -0
- {atomic_agents-2.6.0/atomic-forge/tools/youtube_transcript_scraper → atomic_agents-2.7.3/atomic-forge/tools/tavily_search}/.coveragerc +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/tavily_search/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/tavily_search/pyproject.toml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/tavily_search/requirements.txt +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/tavily_search/tests/test_tavily_seach.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/tavily_search/tool/tavily_search.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/webpage_scraper/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/webpage_scraper/pyproject.toml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/webpage_scraper/requirements.txt +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/webpage_scraper/tests/conftest.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/webpage_scraper/tests/test_webpage_scraper.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/webpage_scraper/tool/webpage_scraper.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/youtube_transcript_scraper/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/youtube_transcript_scraper/pyproject.toml +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/youtube_transcript_scraper/requirements.txt +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/youtube_transcript_scraper/tests/test_youtube_transcript_scraper.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-forge/tools/youtube_transcript_scraper/tool/youtube_transcript_scraper.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/build_and_deploy.ps1 +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/claude-plugin/atomic-agents/.claude-plugin/plugin.json +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/claude-plugin/atomic-agents/CHANGELOG.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/claude-plugin/atomic-agents/LICENSE +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/claude-plugin/atomic-agents/README.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/claude-plugin/atomic-agents/agents/atomic-architect.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/claude-plugin/atomic-agents/agents/atomic-explorer.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/claude-plugin/atomic-agents/agents/atomic-reviewer.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/claude-plugin/atomic-agents/agents/schema-designer.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/claude-plugin/atomic-agents/commands/atomic-agent.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/claude-plugin/atomic-agents/commands/atomic-create.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/claude-plugin/atomic-agents/commands/atomic-tool.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/claude-plugin/atomic-agents/skills/atomic-agents/SKILL.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/claude-plugin/atomic-agents/skills/atomic-agents/examples/basic-agent.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/claude-plugin/atomic-agents/skills/atomic-context/SKILL.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/claude-plugin/atomic-agents/skills/atomic-context/examples/rag-context.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/claude-plugin/atomic-agents/skills/atomic-prompts/SKILL.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/claude-plugin/atomic-agents/skills/atomic-schemas/SKILL.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/claude-plugin/atomic-agents/skills/atomic-schemas/examples/common-schemas.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/claude-plugin/atomic-agents/skills/atomic-structure/SKILL.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/claude-plugin/atomic-agents/skills/atomic-tools/SKILL.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/claude-plugin/atomic-agents/skills/atomic-tools/examples/api-tool.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/.nojekyll +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/Makefile +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/_static/logo.png +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/api/agents.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/api/context.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/api/index.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/api/utils.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/conf.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/contributing.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/examples/index.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/examples/mcp_agent.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/guides/cookbook.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/guides/deployment.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/guides/error-handling.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/guides/faq.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/guides/hooks.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/guides/index.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/guides/logging.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/guides/memory.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/guides/orchestration.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/guides/performance.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/guides/quickstart.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/guides/security.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/guides/testing.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/index.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/docs/make.bat +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/greptile.json +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/guides/DEV_GUIDE.md +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/pytest.ini +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/requirements.txt +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/scripts/generate_llms_files.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/scripts/sync_version.py +0 -0
- {atomic_agents-2.6.0 → atomic_agents-2.7.3}/setup.py +0 -0
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: release
|
|
3
|
+
description: Release a new version of atomic-agents to PyPI and GitHub. Use when the user asks to "release", "publish", "deploy", or "bump version" for atomic-agents.
|
|
4
|
+
allowed-tools: Read, Bash, Grep, Glob
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Release Process for atomic-agents
|
|
8
|
+
|
|
9
|
+
## Overview
|
|
10
|
+
|
|
11
|
+
This skill guides the release process for atomic-agents, including version bumping, PyPI publishing, and GitHub release creation.
|
|
12
|
+
|
|
13
|
+
## Prerequisites
|
|
14
|
+
|
|
15
|
+
- Must be on `main` branch with clean working directory
|
|
16
|
+
- `.env` file must contain `PYPI_TOKEN` environment variable
|
|
17
|
+
- Must have push access to the repository
|
|
18
|
+
|
|
19
|
+
## Release Types
|
|
20
|
+
|
|
21
|
+
| Type | When to Use | Example |
|
|
22
|
+
|------|-------------|---------|
|
|
23
|
+
| `major` | Breaking changes | 2.5.0 → 3.0.0 |
|
|
24
|
+
| `minor` | New features (backwards compatible) | 2.5.0 → 2.6.0 |
|
|
25
|
+
| `patch` | Bug fixes only | 2.5.0 → 2.5.1 |
|
|
26
|
+
|
|
27
|
+
## Step-by-Step Process
|
|
28
|
+
|
|
29
|
+
### 1. Prepare the Branch
|
|
30
|
+
|
|
31
|
+
```bash
|
|
32
|
+
git checkout main
|
|
33
|
+
git pull
|
|
34
|
+
git status # Ensure clean working directory
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### 2. Run Build and Deploy Script
|
|
38
|
+
|
|
39
|
+
**Important**: The script bumps versions, so if it fails partway through, reset to main before retrying.
|
|
40
|
+
|
|
41
|
+
```powershell
|
|
42
|
+
powershell -ExecutionPolicy Bypass -File build_and_deploy.ps1 <major|minor|patch>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
This script will:
|
|
46
|
+
- Read current version from `pyproject.toml`
|
|
47
|
+
- Increment version based on release type
|
|
48
|
+
- Update `pyproject.toml` with new version
|
|
49
|
+
- Run `uv sync` to update dependencies
|
|
50
|
+
- Run `uv build` to create distribution packages
|
|
51
|
+
- Run `uv publish` to upload to PyPI
|
|
52
|
+
|
|
53
|
+
### 3. If Script Fails - Reset and Retry
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
git checkout main
|
|
57
|
+
git reset --hard origin/main
|
|
58
|
+
# Fix the issue, then run script again
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### 4. Commit and Push Version Bump
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
git add pyproject.toml uv.lock
|
|
65
|
+
git commit -m "Release vX.Y.Z"
|
|
66
|
+
git push
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 5. Create GitHub Release
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
gh release create vX.Y.Z --title "vX.Y.Z" --notes "RELEASE_NOTES_HERE"
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Release Notes Template
|
|
76
|
+
|
|
77
|
+
```markdown
|
|
78
|
+
## What's New
|
|
79
|
+
|
|
80
|
+
### Feature Name
|
|
81
|
+
Brief description of the feature.
|
|
82
|
+
|
|
83
|
+
#### Features
|
|
84
|
+
- Feature 1
|
|
85
|
+
- Feature 2
|
|
86
|
+
|
|
87
|
+
#### Improvements
|
|
88
|
+
- Improvement 1
|
|
89
|
+
|
|
90
|
+
#### Bug Fixes
|
|
91
|
+
- Fix 1
|
|
92
|
+
|
|
93
|
+
### Full Changelog
|
|
94
|
+
https://github.com/BrainBlend-AI/atomic-agents/compare/vOLD...vNEW
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Checklist
|
|
98
|
+
|
|
99
|
+
- [ ] On main branch with clean working directory
|
|
100
|
+
- [ ] `.env` file has `PYPI_TOKEN`
|
|
101
|
+
- [ ] Determined correct release type (major/minor/patch)
|
|
102
|
+
- [ ] Build and deploy script completed successfully
|
|
103
|
+
- [ ] Version bump committed and pushed
|
|
104
|
+
- [ ] GitHub release created with release notes
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
- repo: https://github.com/pre-commit/pre-commit-hooks
|
|
3
|
+
rev: v5.0.0
|
|
4
|
+
hooks:
|
|
5
|
+
- id: trailing-whitespace
|
|
6
|
+
- id: end-of-file-fixer
|
|
7
|
+
- id: check-yaml
|
|
8
|
+
- id: check-added-large-files
|
|
9
|
+
|
|
10
|
+
- repo: https://github.com/psf/black
|
|
11
|
+
rev: 24.10.0
|
|
12
|
+
hooks:
|
|
13
|
+
- id: black
|
|
14
|
+
args: [--line-length=127]
|
|
15
|
+
|
|
16
|
+
- repo: https://github.com/pycqa/flake8
|
|
17
|
+
rev: 7.1.1
|
|
18
|
+
hooks:
|
|
19
|
+
- id: flake8
|
|
20
|
+
args: [--max-line-length=127]
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: atomic-agents
|
|
3
|
-
Version: 2.
|
|
3
|
+
Version: 2.7.3
|
|
4
4
|
Summary: A versatile framework for creating and managing intelligent agents.
|
|
5
5
|
Project-URL: Homepage, https://github.com/BrainBlend-AI/atomic-agents
|
|
6
6
|
Project-URL: Repository, https://github.com/BrainBlend-AI/atomic-agents
|
{atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/atomic_agents/context/chat_history.py
RENAMED
|
@@ -97,64 +97,92 @@ class ChatHistory:
|
|
|
97
97
|
either a single JSON string or a mixed array of JSON and multimodal objects.
|
|
98
98
|
|
|
99
99
|
Note:
|
|
100
|
-
This method supports multimodal content
|
|
101
|
-
|
|
100
|
+
This method supports multimodal content at any nesting depth by
|
|
101
|
+
recursively extracting multimodal objects and using Pydantic's
|
|
102
|
+
model_dump_json(exclude=...) for proper serialization of remaining fields.
|
|
102
103
|
"""
|
|
103
104
|
history = []
|
|
104
105
|
for message in self.history:
|
|
105
106
|
input_content = message.content
|
|
107
|
+
multimodal_objects, exclude_spec = self._extract_multimodal_info(input_content)
|
|
106
108
|
|
|
107
|
-
|
|
108
|
-
multimodal_objects = []
|
|
109
|
-
has_multimodal = False
|
|
110
|
-
|
|
111
|
-
# Extract multimodal content first
|
|
112
|
-
for field_name, field in input_content.__class__.model_fields.items():
|
|
113
|
-
field_value = getattr(input_content, field_name)
|
|
114
|
-
|
|
115
|
-
if isinstance(field_value, list):
|
|
116
|
-
for item in field_value:
|
|
117
|
-
if isinstance(item, INSTRUCTOR_MULTIMODAL_TYPES):
|
|
118
|
-
multimodal_objects.append(item)
|
|
119
|
-
has_multimodal = True
|
|
120
|
-
elif isinstance(field_value, INSTRUCTOR_MULTIMODAL_TYPES):
|
|
121
|
-
multimodal_objects.append(field_value)
|
|
122
|
-
has_multimodal = True
|
|
123
|
-
|
|
124
|
-
if has_multimodal:
|
|
125
|
-
# For multimodal content: create mixed array with JSON + multimodal objects
|
|
109
|
+
if multimodal_objects:
|
|
126
110
|
processed_content = []
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
for field_name, field in input_content.__class__.model_fields.items():
|
|
131
|
-
field_value = getattr(input_content, field_name)
|
|
132
|
-
|
|
133
|
-
if isinstance(field_value, list):
|
|
134
|
-
# Only include non-multimodal items from lists
|
|
135
|
-
non_multimodal_items = [
|
|
136
|
-
item for item in field_value if not isinstance(item, INSTRUCTOR_MULTIMODAL_TYPES)
|
|
137
|
-
]
|
|
138
|
-
if non_multimodal_items:
|
|
139
|
-
non_multimodal_data[field_name] = non_multimodal_items
|
|
140
|
-
elif not isinstance(field_value, INSTRUCTOR_MULTIMODAL_TYPES):
|
|
141
|
-
non_multimodal_data[field_name] = field_value
|
|
142
|
-
|
|
143
|
-
# Add single JSON string if there are non-multimodal fields
|
|
144
|
-
if non_multimodal_data:
|
|
145
|
-
processed_content.append(json.dumps(non_multimodal_data, ensure_ascii=False))
|
|
146
|
-
|
|
147
|
-
# Add all multimodal objects
|
|
111
|
+
content_json = input_content.model_dump_json(exclude=exclude_spec)
|
|
112
|
+
if content_json and content_json != "{}":
|
|
113
|
+
processed_content.append(content_json)
|
|
148
114
|
processed_content.extend(multimodal_objects)
|
|
149
|
-
|
|
150
115
|
history.append({"role": message.role, "content": processed_content})
|
|
151
116
|
else:
|
|
152
|
-
# No multimodal content: generate single cohesive JSON string
|
|
153
117
|
content_json = input_content.model_dump_json()
|
|
154
118
|
history.append({"role": message.role, "content": content_json})
|
|
155
119
|
|
|
156
120
|
return history
|
|
157
121
|
|
|
122
|
+
@staticmethod
|
|
123
|
+
def _extract_multimodal_info(obj):
|
|
124
|
+
"""
|
|
125
|
+
Recursively extract multimodal objects and build a Pydantic-compatible exclude spec.
|
|
126
|
+
|
|
127
|
+
Walks the object tree to find all Instructor multimodal types (Image, Audio, PDF)
|
|
128
|
+
at any nesting depth, collecting them into a flat list and building an exclude
|
|
129
|
+
specification that can be passed to model_dump_json(exclude=...).
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
obj: The object to inspect (BaseIOSchema, list, dict, or primitive).
|
|
133
|
+
|
|
134
|
+
Returns:
|
|
135
|
+
tuple: (multimodal_objects, exclude_spec) where:
|
|
136
|
+
- multimodal_objects: flat list of all multimodal objects found
|
|
137
|
+
- exclude_spec: Pydantic exclude dict, True (exclude entirely), or None
|
|
138
|
+
"""
|
|
139
|
+
if isinstance(obj, INSTRUCTOR_MULTIMODAL_TYPES):
|
|
140
|
+
return [obj], True
|
|
141
|
+
|
|
142
|
+
if hasattr(obj, "__class__") and hasattr(obj.__class__, "model_fields"):
|
|
143
|
+
all_objects = []
|
|
144
|
+
exclude = {}
|
|
145
|
+
for field_name in obj.__class__.model_fields:
|
|
146
|
+
if hasattr(obj, field_name):
|
|
147
|
+
field_value = getattr(obj, field_name)
|
|
148
|
+
objects, sub_exclude = ChatHistory._extract_multimodal_info(field_value)
|
|
149
|
+
if objects:
|
|
150
|
+
all_objects.extend(objects)
|
|
151
|
+
exclude[field_name] = sub_exclude
|
|
152
|
+
return all_objects, (exclude if exclude else None)
|
|
153
|
+
|
|
154
|
+
if isinstance(obj, (list, tuple)):
|
|
155
|
+
all_objects = []
|
|
156
|
+
exclude = {}
|
|
157
|
+
for i, item in enumerate(obj):
|
|
158
|
+
objects, sub_exclude = ChatHistory._extract_multimodal_info(item)
|
|
159
|
+
if objects:
|
|
160
|
+
all_objects.extend(objects)
|
|
161
|
+
exclude[i] = sub_exclude
|
|
162
|
+
if not all_objects:
|
|
163
|
+
return [], None
|
|
164
|
+
# If every item in the list is fully multimodal, exclude the entire field
|
|
165
|
+
if len(exclude) == len(obj) and all(v is True for v in exclude.values()):
|
|
166
|
+
return all_objects, True
|
|
167
|
+
return all_objects, exclude
|
|
168
|
+
|
|
169
|
+
if isinstance(obj, dict):
|
|
170
|
+
all_objects = []
|
|
171
|
+
exclude = {}
|
|
172
|
+
for k, v in obj.items():
|
|
173
|
+
objects, sub_exclude = ChatHistory._extract_multimodal_info(v)
|
|
174
|
+
if objects:
|
|
175
|
+
all_objects.extend(objects)
|
|
176
|
+
exclude[k] = sub_exclude
|
|
177
|
+
if not all_objects:
|
|
178
|
+
return [], None
|
|
179
|
+
# If every value in the dict is fully multimodal, exclude the entire field
|
|
180
|
+
if len(exclude) == len(obj) and all(v is True for v in exclude.values()):
|
|
181
|
+
return all_objects, True
|
|
182
|
+
return all_objects, exclude
|
|
183
|
+
|
|
184
|
+
return [], None
|
|
185
|
+
|
|
158
186
|
def copy(self) -> "ChatHistory":
|
|
159
187
|
"""
|
|
160
188
|
Creates a copy of the chat history.
|
{atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/atomic_agents/utils/token_counter.py
RENAMED
|
@@ -147,12 +147,15 @@ class TokenCounter:
|
|
|
147
147
|
raise TypeError(f"model must be a string, got {type(model).__name__}")
|
|
148
148
|
|
|
149
149
|
try:
|
|
150
|
-
from litellm import
|
|
150
|
+
from litellm import get_model_info
|
|
151
151
|
except ImportError as e:
|
|
152
152
|
raise ImportError("litellm is required for token counting. " "Install it with: pip install litellm") from e
|
|
153
153
|
|
|
154
154
|
try:
|
|
155
|
-
|
|
155
|
+
info = get_model_info(model)
|
|
156
|
+
# Use max_input_tokens (context window) not max_tokens (output limit)
|
|
157
|
+
max_input = info.get("max_input_tokens")
|
|
158
|
+
return max_input if max_input is not None else info.get("max_tokens")
|
|
156
159
|
except Exception as e:
|
|
157
160
|
logger.warning(f"Could not determine max tokens for model '{model}': {e}")
|
|
158
161
|
return None
|
{atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/tests/connectors/mcp/test_mcp_factory.py
RENAMED
|
@@ -207,9 +207,7 @@ async def test_typed_output_schema_with_structured_content_dict(monkeypatch):
|
|
|
207
207
|
"required": ["results", "count"],
|
|
208
208
|
}
|
|
209
209
|
definitions = [
|
|
210
|
-
MCPToolDefinition(
|
|
211
|
-
name="SearchTool", description="Search tool", input_schema=input_schema, output_schema=output_schema
|
|
212
|
-
)
|
|
210
|
+
MCPToolDefinition(name="SearchTool", description="Search tool", input_schema=input_schema, output_schema=output_schema)
|
|
213
211
|
]
|
|
214
212
|
monkeypatch.setattr(MCPFactory, "_fetch_tool_definitions", lambda self: definitions)
|
|
215
213
|
tools = fetch_mcp_tools("http://example.com", MCPTransportType.HTTP_STREAM)
|
|
@@ -218,9 +218,7 @@ class TestCreateModelFromSchema:
|
|
|
218
218
|
},
|
|
219
219
|
"required": ["results", "count"],
|
|
220
220
|
}
|
|
221
|
-
model = SchemaTransformer.create_model_from_schema(
|
|
222
|
-
schema, "OutputModel", "my_tool", is_output_schema=True
|
|
223
|
-
)
|
|
221
|
+
model = SchemaTransformer.create_model_from_schema(schema, "OutputModel", "my_tool", is_output_schema=True)
|
|
224
222
|
|
|
225
223
|
# Output schema should NOT have tool_name field
|
|
226
224
|
assert "tool_name" not in model.model_fields
|
|
@@ -243,9 +241,7 @@ class TestCreateModelFromSchema:
|
|
|
243
241
|
},
|
|
244
242
|
"required": ["query"],
|
|
245
243
|
}
|
|
246
|
-
model = SchemaTransformer.create_model_from_schema(
|
|
247
|
-
schema, "InputModel", "my_tool", is_output_schema=False
|
|
248
|
-
)
|
|
244
|
+
model = SchemaTransformer.create_model_from_schema(schema, "InputModel", "my_tool", is_output_schema=False)
|
|
249
245
|
|
|
250
246
|
# Input schema SHOULD have tool_name field
|
|
251
247
|
assert "tool_name" in model.model_fields
|
|
@@ -271,9 +267,7 @@ class TestCreateModelFromSchema:
|
|
|
271
267
|
|
|
272
268
|
# Output schema for resource - should not have resource_name
|
|
273
269
|
model = SchemaTransformer.create_model_from_schema(
|
|
274
|
-
schema, "ResourceOutput", "my_resource",
|
|
275
|
-
attribute_type=MCPAttributeType.RESOURCE,
|
|
276
|
-
is_output_schema=True
|
|
270
|
+
schema, "ResourceOutput", "my_resource", attribute_type=MCPAttributeType.RESOURCE, is_output_schema=True
|
|
277
271
|
)
|
|
278
272
|
|
|
279
273
|
assert "resource_name" not in model.model_fields
|
{atomic_agents-2.6.0 → atomic_agents-2.7.3}/atomic-agents/tests/context/test_chat_history.py
RENAMED
|
@@ -538,3 +538,330 @@ def test_process_multimodal_paths_comprehensive():
|
|
|
538
538
|
assert loaded_content.images[0].source == Path("test/sample.jpg")
|
|
539
539
|
assert isinstance(loaded_content.pdfs[0].source, Path)
|
|
540
540
|
assert loaded_content.pdfs[0].source == Path("test/doc.pdf")
|
|
541
|
+
|
|
542
|
+
|
|
543
|
+
def test_get_history_nested_pydantic_with_toplevel_multimodal(history):
|
|
544
|
+
"""Issue #208: nested Pydantic model + top-level multimodal causes json.dumps TypeError"""
|
|
545
|
+
|
|
546
|
+
class ContextInfo(BaseIOSchema):
|
|
547
|
+
"""Nested context info."""
|
|
548
|
+
|
|
549
|
+
label: str = Field(..., description="Label")
|
|
550
|
+
value: str = Field(..., description="Value")
|
|
551
|
+
|
|
552
|
+
class AgentInput(BaseIOSchema):
|
|
553
|
+
"""Input with multimodal and nested schema."""
|
|
554
|
+
|
|
555
|
+
instruction: str = Field(..., description="Instruction text")
|
|
556
|
+
images: List[instructor.Image] = Field(..., description="Images")
|
|
557
|
+
context: ContextInfo = Field(..., description="Nested context")
|
|
558
|
+
|
|
559
|
+
mock_image = instructor.Image(source="test_url", media_type="image/jpeg", detail="low")
|
|
560
|
+
context = ContextInfo(label="example", value="nested")
|
|
561
|
+
content = AgentInput(instruction="Do something", images=[mock_image], context=context)
|
|
562
|
+
|
|
563
|
+
history.add_message("user", content)
|
|
564
|
+
result = history.get_history()
|
|
565
|
+
|
|
566
|
+
assert len(result) == 1
|
|
567
|
+
assert result[0]["role"] == "user"
|
|
568
|
+
assert isinstance(result[0]["content"], list)
|
|
569
|
+
|
|
570
|
+
json_part = json.loads(result[0]["content"][0])
|
|
571
|
+
assert json_part["instruction"] == "Do something"
|
|
572
|
+
assert json_part["context"] == {"label": "example", "value": "nested"}
|
|
573
|
+
assert "images" not in json_part
|
|
574
|
+
assert result[0]["content"][1] == mock_image
|
|
575
|
+
|
|
576
|
+
|
|
577
|
+
def test_get_history_deeply_nested_multimodal_only(history):
|
|
578
|
+
"""Issue #141: multimodal inside nested schema with no top-level multimodal"""
|
|
579
|
+
|
|
580
|
+
class Document(BaseIOSchema):
|
|
581
|
+
"""PDF document with owner."""
|
|
582
|
+
|
|
583
|
+
pdf: instructor.multimodal.PDF = Field(..., description="The PDF data")
|
|
584
|
+
owner: str = Field(..., description="The PDF owner")
|
|
585
|
+
|
|
586
|
+
class InputSchema(BaseIOSchema):
|
|
587
|
+
"""A list of documents to analyze."""
|
|
588
|
+
|
|
589
|
+
documents: List[Document] = Field(..., description="List of documents")
|
|
590
|
+
instruction: str = Field(..., description="What to do")
|
|
591
|
+
|
|
592
|
+
mock_pdf = instructor.multimodal.PDF(source="test_pdf_url", media_type="application/pdf", detail="low")
|
|
593
|
+
content = InputSchema(
|
|
594
|
+
documents=[Document(pdf=mock_pdf, owner="Alice")],
|
|
595
|
+
instruction="Analyze these",
|
|
596
|
+
)
|
|
597
|
+
|
|
598
|
+
history.add_message("user", content)
|
|
599
|
+
result = history.get_history()
|
|
600
|
+
|
|
601
|
+
assert len(result) == 1
|
|
602
|
+
assert isinstance(result[0]["content"], list)
|
|
603
|
+
|
|
604
|
+
json_part = json.loads(result[0]["content"][0])
|
|
605
|
+
assert json_part["instruction"] == "Analyze these"
|
|
606
|
+
assert json_part["documents"] == [{"owner": "Alice"}]
|
|
607
|
+
assert result[0]["content"][1] == mock_pdf
|
|
608
|
+
|
|
609
|
+
|
|
610
|
+
def test_get_history_mixed_nested_and_toplevel_multimodal(history):
|
|
611
|
+
"""Both nested and top-level multimodal content"""
|
|
612
|
+
|
|
613
|
+
class Attachment(BaseIOSchema):
|
|
614
|
+
"""An attachment with an image."""
|
|
615
|
+
|
|
616
|
+
image: instructor.Image = Field(..., description="Attached image")
|
|
617
|
+
caption: str = Field(..., description="Caption")
|
|
618
|
+
|
|
619
|
+
class MessageInput(BaseIOSchema):
|
|
620
|
+
"""Message with both nested and top-level multimodal."""
|
|
621
|
+
|
|
622
|
+
text: str = Field(..., description="Message text")
|
|
623
|
+
inline_image: instructor.Image = Field(..., description="Inline image")
|
|
624
|
+
attachment: Attachment = Field(..., description="An attachment")
|
|
625
|
+
|
|
626
|
+
img1 = instructor.Image(source="inline_url", media_type="image/jpeg", detail="low")
|
|
627
|
+
img2 = instructor.Image(source="attached_url", media_type="image/png", detail="low")
|
|
628
|
+
content = MessageInput(
|
|
629
|
+
text="Check this out",
|
|
630
|
+
inline_image=img1,
|
|
631
|
+
attachment=Attachment(image=img2, caption="See here"),
|
|
632
|
+
)
|
|
633
|
+
|
|
634
|
+
history.add_message("user", content)
|
|
635
|
+
result = history.get_history()
|
|
636
|
+
|
|
637
|
+
assert len(result) == 1
|
|
638
|
+
assert isinstance(result[0]["content"], list)
|
|
639
|
+
|
|
640
|
+
assert len(result[0]["content"]) == 3 # JSON + 2 images
|
|
641
|
+
json_part = json.loads(result[0]["content"][0])
|
|
642
|
+
assert json_part["text"] == "Check this out"
|
|
643
|
+
assert json_part["attachment"] == {"caption": "See here"}
|
|
644
|
+
assert "inline_image" not in json_part
|
|
645
|
+
assert img1 in result[0]["content"]
|
|
646
|
+
assert img2 in result[0]["content"]
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
def test_get_history_list_of_nested_schemas_with_multimodal(history):
|
|
650
|
+
"""Multiple nested schemas each containing multimodal objects"""
|
|
651
|
+
|
|
652
|
+
class Document(BaseIOSchema):
|
|
653
|
+
"""A document with PDF."""
|
|
654
|
+
|
|
655
|
+
pdf: instructor.multimodal.PDF = Field(..., description="The PDF")
|
|
656
|
+
title: str = Field(..., description="Document title")
|
|
657
|
+
|
|
658
|
+
class BatchInput(BaseIOSchema):
|
|
659
|
+
"""Batch of documents."""
|
|
660
|
+
|
|
661
|
+
documents: List[Document] = Field(..., description="Documents to process")
|
|
662
|
+
|
|
663
|
+
pdf1 = instructor.multimodal.PDF(source="doc1.pdf", media_type="application/pdf", detail="low")
|
|
664
|
+
pdf2 = instructor.multimodal.PDF(source="doc2.pdf", media_type="application/pdf", detail="low")
|
|
665
|
+
content = BatchInput(
|
|
666
|
+
documents=[
|
|
667
|
+
Document(pdf=pdf1, title="First"),
|
|
668
|
+
Document(pdf=pdf2, title="Second"),
|
|
669
|
+
]
|
|
670
|
+
)
|
|
671
|
+
|
|
672
|
+
history.add_message("user", content)
|
|
673
|
+
result = history.get_history()
|
|
674
|
+
|
|
675
|
+
assert len(result) == 1
|
|
676
|
+
assert isinstance(result[0]["content"], list)
|
|
677
|
+
|
|
678
|
+
assert len(result[0]["content"]) == 3 # JSON + 2 PDFs
|
|
679
|
+
json_part = json.loads(result[0]["content"][0])
|
|
680
|
+
assert json_part["documents"] == [{"title": "First"}, {"title": "Second"}]
|
|
681
|
+
assert pdf1 in result[0]["content"]
|
|
682
|
+
assert pdf2 in result[0]["content"]
|
|
683
|
+
|
|
684
|
+
|
|
685
|
+
def test_get_history_only_multimodal_fields(history):
|
|
686
|
+
"""Schema where ALL fields are multimodal - JSON should be omitted"""
|
|
687
|
+
|
|
688
|
+
class ImagesOnly(BaseIOSchema):
|
|
689
|
+
"""Only images."""
|
|
690
|
+
|
|
691
|
+
images: List[instructor.Image] = Field(..., description="Images")
|
|
692
|
+
|
|
693
|
+
img1 = instructor.Image(source="url1", media_type="image/jpeg", detail="low")
|
|
694
|
+
img2 = instructor.Image(source="url2", media_type="image/jpeg", detail="low")
|
|
695
|
+
content = ImagesOnly(images=[img1, img2])
|
|
696
|
+
|
|
697
|
+
history.add_message("user", content)
|
|
698
|
+
result = history.get_history()
|
|
699
|
+
|
|
700
|
+
assert len(result) == 1
|
|
701
|
+
assert isinstance(result[0]["content"], list)
|
|
702
|
+
# No JSON string should be present since all fields are multimodal
|
|
703
|
+
assert len(result[0]["content"]) == 2
|
|
704
|
+
assert all(not isinstance(item, str) for item in result[0]["content"])
|
|
705
|
+
assert img1 in result[0]["content"]
|
|
706
|
+
assert img2 in result[0]["content"]
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
def test_get_history_no_multimodal_unchanged(history):
|
|
710
|
+
"""Non-multimodal schemas should work exactly as before"""
|
|
711
|
+
|
|
712
|
+
class SimpleInput(BaseIOSchema):
|
|
713
|
+
"""Simple input."""
|
|
714
|
+
|
|
715
|
+
text: str = Field(..., description="Text")
|
|
716
|
+
count: int = Field(..., description="Count")
|
|
717
|
+
|
|
718
|
+
content = SimpleInput(text="hello", count=42)
|
|
719
|
+
history.add_message("user", content)
|
|
720
|
+
result = history.get_history()
|
|
721
|
+
|
|
722
|
+
assert len(result) == 1
|
|
723
|
+
assert result[0]["role"] == "user"
|
|
724
|
+
assert isinstance(result[0]["content"], str)
|
|
725
|
+
assert json.loads(result[0]["content"]) == {"text": "hello", "count": 42}
|
|
726
|
+
|
|
727
|
+
|
|
728
|
+
# ---------------------------------------------------------------------------
|
|
729
|
+
# Direct unit tests for _extract_multimodal_info
|
|
730
|
+
# ---------------------------------------------------------------------------
|
|
731
|
+
|
|
732
|
+
|
|
733
|
+
def test_extract_multimodal_info_plain_schema():
|
|
734
|
+
"""No multimodal content returns empty list and None exclude spec"""
|
|
735
|
+
|
|
736
|
+
class Plain(BaseIOSchema):
|
|
737
|
+
"""Plain schema."""
|
|
738
|
+
|
|
739
|
+
text: str = Field(..., description="Text")
|
|
740
|
+
|
|
741
|
+
objs, spec = ChatHistory._extract_multimodal_info(Plain(text="hello"))
|
|
742
|
+
assert objs == []
|
|
743
|
+
assert spec is None
|
|
744
|
+
|
|
745
|
+
|
|
746
|
+
def test_extract_multimodal_info_toplevel_image():
|
|
747
|
+
"""Top-level Image returns the object and True exclude spec"""
|
|
748
|
+
img = instructor.Image(source="url", media_type="image/jpeg", detail="low")
|
|
749
|
+
objs, spec = ChatHistory._extract_multimodal_info(img)
|
|
750
|
+
assert objs == [img]
|
|
751
|
+
assert spec is True
|
|
752
|
+
|
|
753
|
+
|
|
754
|
+
def test_extract_multimodal_info_nested_schema():
|
|
755
|
+
"""Nested schema with multimodal returns correct exclude spec shape"""
|
|
756
|
+
|
|
757
|
+
class Inner(BaseIOSchema):
|
|
758
|
+
"""Inner schema."""
|
|
759
|
+
|
|
760
|
+
image: instructor.Image = Field(..., description="Image")
|
|
761
|
+
label: str = Field(..., description="Label")
|
|
762
|
+
|
|
763
|
+
class Outer(BaseIOSchema):
|
|
764
|
+
"""Outer schema."""
|
|
765
|
+
|
|
766
|
+
inner: Inner = Field(..., description="Inner")
|
|
767
|
+
text: str = Field(..., description="Text")
|
|
768
|
+
|
|
769
|
+
img = instructor.Image(source="url", media_type="image/jpeg", detail="low")
|
|
770
|
+
objs, spec = ChatHistory._extract_multimodal_info(Outer(inner=Inner(image=img, label="test"), text="hello"))
|
|
771
|
+
|
|
772
|
+
assert objs == [img]
|
|
773
|
+
# Exclude spec should be {"inner": {"image": True}}
|
|
774
|
+
assert spec == {"inner": {"image": True}}
|
|
775
|
+
|
|
776
|
+
|
|
777
|
+
def test_extract_multimodal_info_list_all_multimodal():
|
|
778
|
+
"""List where every item is multimodal collapses to True"""
|
|
779
|
+
|
|
780
|
+
class Schema(BaseIOSchema):
|
|
781
|
+
"""Schema with all-multimodal list."""
|
|
782
|
+
|
|
783
|
+
images: List[instructor.Image] = Field(..., description="Images")
|
|
784
|
+
|
|
785
|
+
img1 = instructor.Image(source="url1", media_type="image/jpeg", detail="low")
|
|
786
|
+
img2 = instructor.Image(source="url2", media_type="image/jpeg", detail="low")
|
|
787
|
+
objs, spec = ChatHistory._extract_multimodal_info(Schema(images=[img1, img2]))
|
|
788
|
+
|
|
789
|
+
assert objs == [img1, img2]
|
|
790
|
+
# All items multimodal → field excluded entirely
|
|
791
|
+
assert spec == {"images": True}
|
|
792
|
+
|
|
793
|
+
|
|
794
|
+
def test_extract_multimodal_info_list_partial_multimodal():
|
|
795
|
+
"""List with mixed content returns index-based exclude spec"""
|
|
796
|
+
|
|
797
|
+
class Schema(BaseIOSchema):
|
|
798
|
+
"""Schema with mixed list."""
|
|
799
|
+
|
|
800
|
+
items: List[Union[str, instructor.Image]] = Field(..., description="Items")
|
|
801
|
+
|
|
802
|
+
img = instructor.Image(source="url", media_type="image/jpeg", detail="low")
|
|
803
|
+
objs, spec = ChatHistory._extract_multimodal_info(Schema(items=["text", img]))
|
|
804
|
+
|
|
805
|
+
assert objs == [img]
|
|
806
|
+
# Only index 1 is multimodal
|
|
807
|
+
assert spec == {"items": {1: True}}
|
|
808
|
+
|
|
809
|
+
|
|
810
|
+
def test_extract_multimodal_info_tuple_support():
|
|
811
|
+
"""Tuples are handled identically to lists"""
|
|
812
|
+
img = instructor.Image(source="url", media_type="image/jpeg", detail="low")
|
|
813
|
+
objs, spec = ChatHistory._extract_multimodal_info((img, "text"))
|
|
814
|
+
assert objs == [img]
|
|
815
|
+
assert spec == {0: True}
|
|
816
|
+
|
|
817
|
+
|
|
818
|
+
def test_extract_multimodal_info_dict_with_multimodal():
|
|
819
|
+
"""Dict values containing multimodal objects return correct exclude spec"""
|
|
820
|
+
img = instructor.Image(source="url", media_type="image/jpeg", detail="low")
|
|
821
|
+
objs, spec = ChatHistory._extract_multimodal_info({"key1": img, "key2": "text"})
|
|
822
|
+
|
|
823
|
+
assert objs == [img]
|
|
824
|
+
assert spec == {"key1": True}
|
|
825
|
+
|
|
826
|
+
|
|
827
|
+
def test_extract_multimodal_info_dict_all_multimodal():
|
|
828
|
+
"""Dict where all values are multimodal collapses to True"""
|
|
829
|
+
img1 = instructor.Image(source="url1", media_type="image/jpeg", detail="low")
|
|
830
|
+
img2 = instructor.Image(source="url2", media_type="image/jpeg", detail="low")
|
|
831
|
+
objs, spec = ChatHistory._extract_multimodal_info({"a": img1, "b": img2})
|
|
832
|
+
|
|
833
|
+
assert objs == [img1, img2]
|
|
834
|
+
assert spec is True
|
|
835
|
+
|
|
836
|
+
|
|
837
|
+
def test_extract_multimodal_info_dict_no_multimodal():
|
|
838
|
+
"""Dict with no multimodal returns empty list and None"""
|
|
839
|
+
objs, spec = ChatHistory._extract_multimodal_info({"key1": "text", "key2": 42})
|
|
840
|
+
assert objs == []
|
|
841
|
+
assert spec is None
|
|
842
|
+
|
|
843
|
+
|
|
844
|
+
def test_get_history_dict_of_images(history):
|
|
845
|
+
"""Dict[str, Image] field exercises the dict code path in get_history"""
|
|
846
|
+
|
|
847
|
+
class DictImageSchema(BaseIOSchema):
|
|
848
|
+
"""Schema with dict of images."""
|
|
849
|
+
|
|
850
|
+
image_map: Dict[str, instructor.Image] = Field(..., description="Named images")
|
|
851
|
+
note: str = Field(..., description="A note")
|
|
852
|
+
|
|
853
|
+
img_a = instructor.Image(source="url_a", media_type="image/jpeg", detail="low")
|
|
854
|
+
img_b = instructor.Image(source="url_b", media_type="image/jpeg", detail="low")
|
|
855
|
+
content = DictImageSchema(image_map={"front": img_a, "back": img_b}, note="Two views")
|
|
856
|
+
|
|
857
|
+
history.add_message("user", content)
|
|
858
|
+
result = history.get_history()
|
|
859
|
+
|
|
860
|
+
assert len(result) == 1
|
|
861
|
+
assert isinstance(result[0]["content"], list)
|
|
862
|
+
assert len(result[0]["content"]) == 3 # JSON + 2 images
|
|
863
|
+
json_part = json.loads(result[0]["content"][0])
|
|
864
|
+
assert json_part["note"] == "Two views"
|
|
865
|
+
assert "image_map" not in json_part
|
|
866
|
+
assert img_a in result[0]["content"]
|
|
867
|
+
assert img_b in result[0]["content"]
|