kolega-code 0.8.0__tar.gz → 0.8.1__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.
- {kolega_code-0.8.0 → kolega_code-0.8.1}/PKG-INFO +1 -1
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/__init__.py +4 -1
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/__init__.py +6 -1
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/baseagent.py +14 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/browseragent.py +2 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/coder.py +2 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/conversation.py +16 -2
- kolega_code-0.8.1/kolega_code/agent/errors.py +9 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/generalagent.py +2 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/investigationagent.py +2 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/planningagent.py +2 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/test_chatgpt_oauth_provider.py +136 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/test_error_boundary.py +3 -2
- kolega_code-0.8.1/kolega_code/agent/tests/llm/test_openai_responses_provider.py +163 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/test_thinking_effort.py +5 -1
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_base_agent.py +68 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_image_compat.py +56 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/tool_backend/test_agent_tool.py +29 -3
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/agent_tool.py +2 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/llm/client.py +6 -2
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/llm/instrumented_client.py +3 -3
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/llm/models.py +78 -0
- kolega_code-0.8.1/kolega_code/llm/providers/chatgpt_oauth.py +103 -0
- kolega_code-0.8.1/kolega_code/llm/providers/openai_responses.py +60 -0
- kolega_code-0.8.0/kolega_code/llm/providers/chatgpt_oauth.py → kolega_code-0.8.1/kolega_code/llm/providers/responses_common.py +142 -103
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/llm/specs.py +18 -8
- {kolega_code-0.8.0 → kolega_code-0.8.1}/pyproject.toml +1 -1
- {kolega_code-0.8.0 → kolega_code-0.8.1}/uv.lock +2 -2
- {kolega_code-0.8.0 → kolega_code-0.8.1}/.env.example +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/.github/workflows/ci.yml +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/.github/workflows/docs.yml +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/.github/workflows/release.yml +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/.gitignore +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/CONTRIBUTING.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/LICENSE +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/README.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/RELEASING.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/SECURITY.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/conftest.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/.gitignore +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/README.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/astro.config.mjs +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/package-lock.json +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/package.json +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/public/favicon.svg +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/assets/demo.gif +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/assets/kolega-dark.svg +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/assets/kolega-light.svg +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/content/docs/cli/ask.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/content/docs/cli/doctor.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/content/docs/cli/overview.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/content/docs/cli/sessions.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/content/docs/concepts/agents.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/content/docs/concepts/how-it-works.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/content/docs/concepts/tools.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/content/docs/configuration/environment-variables.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/content/docs/configuration/providers-and-models.mdx +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/content/docs/configuration/settings-and-api-keys.mdx +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/content/docs/configuration/sign-in-with-chatgpt.mdx +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/content/docs/getting-started/installation.mdx +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/content/docs/getting-started/quick-start.mdx +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/content/docs/gigacode.mdx +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/content/docs/hooks.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/content/docs/index.mdx +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/content/docs/skills.mdx +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/content/docs/tui/composer.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/content/docs/tui/interface.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/content/docs/tui/modes.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/content/docs/tui/sessions-and-resume.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/content/docs/tui/slash-commands.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/content.config.ts +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/src/styles/brand.css +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/docs/tsconfig.json +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/common.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/compression.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/context.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/orchestration/__init__.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/orchestration/budget.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/orchestration/errors.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/orchestration/executor.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/orchestration/guide.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/orchestration/journal.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/orchestration/runtime.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/orchestration/tests/__init__.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/orchestration/tests/test_orchestration.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/orchestration/types.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompt_provider.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompt_templates/auxiliary/compression/summary.system.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompt_templates/auxiliary/compression/summary.user.md.j2 +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompt_templates/auxiliary/terminal/output_summary.system.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompt_templates/auxiliary/terminal/safety.system.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompt_templates/auxiliary/tools/think_hard.system.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompt_templates/extensions/cli/planning_questions.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompt_templates/extensions/cli/shared_task_list.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompt_templates/extensions/skills/catalog.md.j2 +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompt_templates/system/agents/browser.md.j2 +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompt_templates/system/agents/coder_cli.md.j2 +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompt_templates/system/agents/general.md.j2 +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompt_templates/system/agents/investigation.md.j2 +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompt_templates/system/agents/planning.md.j2 +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompt_templates/system/includes/agents_md_instructions.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompt_templates/system/includes/environment_variables/workspace_env_vars.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompt_templates/system/includes/frontend_guidance.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompt_templates/system/includes/starter_templates/expo.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompt_templates/system/includes/starter_templates/html_website.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompt_templates/system/includes/starter_templates/mern_stack.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompt_templates/system/includes/starter_templates/react_vite_shadcdn.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompt_templates/user_tasks/cli/implement_plan.md.j2 +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompt_templates/user_tasks/cli/init_agents.md.j2 +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/prompts.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/__init__.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/compaction_helpers.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/__init__.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/test_anthropic_token_counting.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/test_billing_openai_cache.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/test_chatgpt_live.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/test_client.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/test_dashscope_mapping.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/test_exceptions.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/test_google_thought_signature.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/test_google_tool_signature_live.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/test_instrumented_client.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/test_instrumented_client_integration.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/test_langfuse_normalization.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/test_live_providers.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/test_model_specs.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/test_openai_cached_tokens.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/test_openai_cached_tokens_stream.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/test_openai_message_conversion.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/test_openai_token_counting.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/test_supports_vision.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/test_tool_execution_ids.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/services/__init__.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/services/test_browser.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/services/test_browser_parity.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/services/test_file_system.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/services/test_sandbox_terminal_input.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/services/test_terminal.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/services/test_terminal_buffer.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/services/test_terminal_state_serializer.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_agent_tools_inventory.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_coder_attachments.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_coder_prompt_extensions.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_commands.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_compaction_integration.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_compaction_persistence.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_compaction_subagent.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_compaction_tui_events.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_compress_command.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_compression.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_conversation_compaction.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_duplicate_tool_results.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_empty_message_handling.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_event_emitter.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_general_agent.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_gigacode_gating.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_hooks_integration.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_html.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_parallel_tool_calls.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_permissions.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_planning_agent.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_prompt_provider.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_prompts.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_read_image_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_tool_registry.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/test_tools.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/tool_backend/__init__.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/tool_backend/test_base_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/tool_backend/test_browser_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/tool_backend/test_build_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/tool_backend/test_create_file_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/tool_backend/test_edit_preview.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/tool_backend/test_glob_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/tool_backend/test_glob_tool_sandbox_parity.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/tool_backend/test_list_directory_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/tool_backend/test_read_file_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/tool_backend/test_replace_entire_file_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/tool_backend/test_replace_lines_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/tool_backend/test_search_and_replace_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/tool_backend/test_search_codebase_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/tool_backend/test_terminal_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/tool_backend/test_think_hard_integration.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/tool_backend/test_think_hard_streaming.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/tool_backend/test_web_fetch_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/tool_backend/test_web_search_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/tool_backend/test_workflow_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/base_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/browser_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/build_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/create_file_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/edit_preview.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/glob_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/list_directory_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/memory_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/read_file_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/read_image_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/replace_entire_file_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/replace_lines_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/search_and_replace_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/search_backends/__init__.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/search_backends/base.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/search_backends/duckduckgo.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/search_backends/errors.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/search_backends/firecrawl.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/search_backends/models.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/search_backends/searxng.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/search_backends/tavily.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/search_codebase_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/streaming_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/terminal_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/think_hard_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/web_fetch_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/web_search_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tool_backend/workflow_tool.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tools.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/utils/commands.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/auth/__init__.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/auth/_jwt.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/auth/chatgpt_oauth.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/auth/constants.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/auth/tests/__init__.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/auth/tests/test_chatgpt_oauth.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/auth/tests/test_tokens.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/auth/tokens.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/__init__.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/app.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/clipboard_image.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/config.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/connection.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/file_index.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/main.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/mentions.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/messages.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/provider_registry.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/session_store.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/settings.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/skills.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/slash_commands.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/tests/__init__.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/tests/test_app.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/tests/test_app_compaction_restore.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/tests/test_app_image_vision_warning.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/tests/test_app_model_switch_vision.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/tests/test_cli_config.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/tests/test_clipboard_image.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/tests/test_connection.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/tests/test_file_index.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/tests/test_main.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/tests/test_mentions.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/tests/test_provider_registry.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/tests/test_session_store.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/tests/test_settings.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/tests/test_skills.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/tests/test_slash_commands.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/tests/test_themes.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/tests/test_updater.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/theme.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/cli/updater.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/config.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/events.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/hooks/__init__.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/hooks/backends.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/hooks/config.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/hooks/dispatcher.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/hooks/events.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/hooks/matcher.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/hooks/outcome.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/hooks/tests/__init__.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/hooks/tests/test_hooks.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/llm/exceptions.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/llm/providers/__init__.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/llm/providers/anthropic.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/llm/providers/base.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/llm/providers/google.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/llm/providers/models.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/llm/providers/openai.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/llm/ratelimit.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/llm/tool_execution_ids.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/models/__init__.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/models/sandbox_terminal_state.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/permissions.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/runtime.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/sandbox/README.md +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/sandbox/__init__.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/sandbox/async_filesystem.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/sandbox/base.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/sandbox/browser.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/sandbox/event_loop.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/sandbox/filesystem.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/sandbox/local.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/sandbox/serializer.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/sandbox/terminal.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/sandbox/utils.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/services/base.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/services/browser.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/services/file_system.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/services/html.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/services/terminal.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/services/terminal_buffer.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/tools/__init__.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/tools/core.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/tools/definitions.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/tools/registry.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/tools/tests/__init__.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/tools/tests/test_definitions.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/utils/__init__.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/utils/images.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/utils/tests/__init__.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/utils/tests/test_images.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/utils/tests/test_images_ascii.py +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/run_tests.sh +0 -0
- {kolega_code-0.8.0 → kolega_code-0.8.1}/scripts/install-kolega-code.sh +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: kolega-code
|
|
3
|
-
Version: 0.8.
|
|
3
|
+
Version: 0.8.1
|
|
4
4
|
Summary: Local-first AI coding agent for the terminal
|
|
5
5
|
Project-URL: Homepage, https://kolega-ai.github.io/kolega-code/
|
|
6
6
|
Project-URL: Documentation, https://kolega-ai.github.io/kolega-code/
|
|
@@ -18,6 +18,7 @@ from .agent.planningagent import PlanningAgent
|
|
|
18
18
|
from .agent.compression import HistoryCompressor
|
|
19
19
|
from .agent.context import AgentContext, AgentServices, Telemetry, WorkspaceInfo
|
|
20
20
|
from .agent.conversation import Conversation
|
|
21
|
+
from .agent.errors import AgentError, MaxAgentIterationsExceeded
|
|
21
22
|
from .events import AgentEventEmitter
|
|
22
23
|
|
|
23
24
|
# Configuration
|
|
@@ -86,6 +87,8 @@ __all__ = [
|
|
|
86
87
|
"AgentContext",
|
|
87
88
|
"AgentServices",
|
|
88
89
|
"Telemetry",
|
|
90
|
+
"AgentError",
|
|
91
|
+
"MaxAgentIterationsExceeded",
|
|
89
92
|
"WorkspaceInfo",
|
|
90
93
|
"Conversation",
|
|
91
94
|
"HistoryCompressor",
|
|
@@ -148,4 +151,4 @@ __all__ = [
|
|
|
148
151
|
"parse_git_status_output",
|
|
149
152
|
]
|
|
150
153
|
|
|
151
|
-
__version__ = "0.8.
|
|
154
|
+
__version__ = "0.8.1"
|
|
@@ -8,6 +8,9 @@ from .browseragent import BrowserAgent
|
|
|
8
8
|
from .generalagent import GeneralAgent
|
|
9
9
|
from .planningagent import PlanningAgent
|
|
10
10
|
|
|
11
|
+
# Export agent errors
|
|
12
|
+
from .errors import AgentError, MaxAgentIterationsExceeded
|
|
13
|
+
|
|
11
14
|
# Export agent models
|
|
12
15
|
from kolega_code.events import AgentStatus, AgentEvent
|
|
13
16
|
|
|
@@ -25,6 +28,8 @@ __all__ = [
|
|
|
25
28
|
"BrowserAgent",
|
|
26
29
|
"GeneralAgent",
|
|
27
30
|
"PlanningAgent",
|
|
31
|
+
"AgentError",
|
|
32
|
+
"MaxAgentIterationsExceeded",
|
|
28
33
|
"AgentStatus",
|
|
29
34
|
"AgentEvent",
|
|
30
35
|
"AgentConfig",
|
|
@@ -39,4 +44,4 @@ __all__ = [
|
|
|
39
44
|
"ToolExtension",
|
|
40
45
|
]
|
|
41
46
|
|
|
42
|
-
__version__ = "0.8.
|
|
47
|
+
__version__ = "0.8.1"
|
|
@@ -12,6 +12,7 @@ from typing import Any, AsyncGenerator, Dict, List, Optional
|
|
|
12
12
|
|
|
13
13
|
from .common import LogMixin
|
|
14
14
|
from .compression import CompactionResult, HistoryCompressor
|
|
15
|
+
from .errors import MaxAgentIterationsExceeded
|
|
15
16
|
from kolega_code.config import AgentConfig, ModelProvider
|
|
16
17
|
from kolega_code.events import AgentConnectionManager
|
|
17
18
|
from .context import AgentContext, AgentServices, Telemetry, WorkspaceInfo
|
|
@@ -119,6 +120,7 @@ class BaseAgent(LogMixin):
|
|
|
119
120
|
sub_agent_recorder: Optional[Any] = None,
|
|
120
121
|
hook_dispatcher: Optional[HookDispatcher] = None,
|
|
121
122
|
context: Optional[AgentContext] = None,
|
|
123
|
+
max_iterations: Optional[int] = None,
|
|
122
124
|
) -> None:
|
|
123
125
|
"""
|
|
124
126
|
Initialize a new BaseAgent instance.
|
|
@@ -203,7 +205,11 @@ class BaseAgent(LogMixin):
|
|
|
203
205
|
if hook_dispatcher is not None:
|
|
204
206
|
context.hook_dispatcher = hook_dispatcher
|
|
205
207
|
|
|
208
|
+
if max_iterations is not None and max_iterations < 1:
|
|
209
|
+
raise ValueError("max_iterations must be a positive integer or None")
|
|
210
|
+
|
|
206
211
|
self.context = context
|
|
212
|
+
self.max_iterations = max_iterations
|
|
207
213
|
|
|
208
214
|
# Flat attributes kept for compatibility with subclasses, tools, and hosts.
|
|
209
215
|
self.project_path = context.workspace.project_path
|
|
@@ -1282,7 +1288,15 @@ class BaseAgent(LogMixin):
|
|
|
1282
1288
|
|
|
1283
1289
|
stop_reason = None
|
|
1284
1290
|
stop_overrides = 0
|
|
1291
|
+
iterations = 0
|
|
1285
1292
|
while stop_reason not in ["end_turn", "max_tokens", "stop_sequence"]:
|
|
1293
|
+
iterations += 1
|
|
1294
|
+
if self.max_iterations is not None and iterations > self.max_iterations:
|
|
1295
|
+
raise MaxAgentIterationsExceeded(
|
|
1296
|
+
f"Agent '{self.agent_name}' exceeded max_iterations={self.max_iterations} "
|
|
1297
|
+
"without reaching a terminal stop reason"
|
|
1298
|
+
)
|
|
1299
|
+
|
|
1286
1300
|
self.mark_cache_checkpoint()
|
|
1287
1301
|
|
|
1288
1302
|
try:
|
|
@@ -45,6 +45,7 @@ class BrowserAgent(BaseAgent):
|
|
|
45
45
|
usage_recorder: Optional[Any] = None,
|
|
46
46
|
sub_agent_recorder: Optional[Any] = None,
|
|
47
47
|
hook_dispatcher: Optional[Any] = None,
|
|
48
|
+
max_iterations: Optional[int] = None,
|
|
48
49
|
) -> None:
|
|
49
50
|
"""
|
|
50
51
|
Initialize a new BrowserAgent instance.
|
|
@@ -97,6 +98,7 @@ class BrowserAgent(BaseAgent):
|
|
|
97
98
|
usage_recorder=usage_recorder,
|
|
98
99
|
sub_agent_recorder=sub_agent_recorder,
|
|
99
100
|
hook_dispatcher=hook_dispatcher,
|
|
101
|
+
max_iterations=max_iterations,
|
|
100
102
|
)
|
|
101
103
|
|
|
102
104
|
self.tool_collection = ToolCollection(
|
|
@@ -49,6 +49,7 @@ class CoderAgent(BaseAgent, LogMixin):
|
|
|
49
49
|
usage_recorder: Optional[Any] = None,
|
|
50
50
|
sub_agent_recorder: Optional[Any] = None,
|
|
51
51
|
hook_dispatcher: Optional[Any] = None,
|
|
52
|
+
max_iterations: Optional[int] = None,
|
|
52
53
|
) -> None:
|
|
53
54
|
"""
|
|
54
55
|
Initialize a new CoderAgent instance.
|
|
@@ -104,6 +105,7 @@ class CoderAgent(BaseAgent, LogMixin):
|
|
|
104
105
|
usage_recorder=usage_recorder,
|
|
105
106
|
sub_agent_recorder=sub_agent_recorder,
|
|
106
107
|
hook_dispatcher=hook_dispatcher,
|
|
108
|
+
max_iterations=max_iterations,
|
|
107
109
|
)
|
|
108
110
|
|
|
109
111
|
# Configure tool collection with custom coder agent tools
|
|
@@ -16,6 +16,7 @@ from kolega_code.llm.models import (
|
|
|
16
16
|
Message,
|
|
17
17
|
MessageHistory,
|
|
18
18
|
RedactedThinkingBlock,
|
|
19
|
+
ResponsesReasoningBlock,
|
|
19
20
|
TextBlock,
|
|
20
21
|
ThinkingBlock,
|
|
21
22
|
ToolCall,
|
|
@@ -113,8 +114,21 @@ def replace_image_blocks_with_placeholders(messages: List[Message], model_name:
|
|
|
113
114
|
return result
|
|
114
115
|
|
|
115
116
|
|
|
117
|
+
# Groups of providers whose reasoning is mutually replayable. The api-key
|
|
118
|
+
# `openai` and the ChatGPT-subscription `openai_chatgpt` are both the OpenAI
|
|
119
|
+
# Responses API, so encrypted reasoning produced by one replays cleanly to the
|
|
120
|
+
# other — the one exception to "only replay reasoning to the same provider".
|
|
121
|
+
_REASONING_COMPATIBLE_GROUPS: tuple[frozenset[str], ...] = (frozenset({"openai", "openai_chatgpt"}),)
|
|
122
|
+
|
|
123
|
+
|
|
116
124
|
def _preserve_reasoning_block(block: Any, *, source_provider: str, target_provider: str) -> bool:
|
|
117
|
-
|
|
125
|
+
if not source_provider:
|
|
126
|
+
return False
|
|
127
|
+
if source_provider == target_provider:
|
|
128
|
+
return True
|
|
129
|
+
return any(
|
|
130
|
+
source_provider in group and target_provider in group for group in _REASONING_COMPATIBLE_GROUPS
|
|
131
|
+
)
|
|
118
132
|
|
|
119
133
|
|
|
120
134
|
def _reasoning_placeholder(block: Any, source_provider: str) -> TextBlock:
|
|
@@ -134,7 +148,7 @@ def _adapt_content_blocks_for_provider(
|
|
|
134
148
|
if isinstance(block, ImageBlock) and not supports_vision:
|
|
135
149
|
adapted.append(TextBlock(text=_image_placeholder(block.media_type, target_model)))
|
|
136
150
|
changed = True
|
|
137
|
-
elif isinstance(block, (ThinkingBlock, RedactedThinkingBlock)):
|
|
151
|
+
elif isinstance(block, (ThinkingBlock, RedactedThinkingBlock, ResponsesReasoningBlock)):
|
|
138
152
|
if _preserve_reasoning_block(block, source_provider=source_provider, target_provider=target_provider):
|
|
139
153
|
adapted.append(block)
|
|
140
154
|
else:
|
|
@@ -47,6 +47,7 @@ class GeneralAgent(BaseAgent):
|
|
|
47
47
|
usage_recorder: Optional[Any] = None,
|
|
48
48
|
sub_agent_recorder: Optional[Any] = None,
|
|
49
49
|
hook_dispatcher: Optional[Any] = None,
|
|
50
|
+
max_iterations: Optional[int] = None,
|
|
50
51
|
) -> None:
|
|
51
52
|
"""
|
|
52
53
|
Initialize a new GeneralAgent instance.
|
|
@@ -99,6 +100,7 @@ class GeneralAgent(BaseAgent):
|
|
|
99
100
|
usage_recorder=usage_recorder,
|
|
100
101
|
sub_agent_recorder=sub_agent_recorder,
|
|
101
102
|
hook_dispatcher=hook_dispatcher,
|
|
103
|
+
max_iterations=max_iterations,
|
|
102
104
|
)
|
|
103
105
|
|
|
104
106
|
# Full coder-style toolset, minus sub-agent dispatch (a dispatched agent
|
|
@@ -45,6 +45,7 @@ class InvestigationAgent(BaseAgent):
|
|
|
45
45
|
usage_recorder: Optional[Any] = None,
|
|
46
46
|
sub_agent_recorder: Optional[Any] = None,
|
|
47
47
|
hook_dispatcher: Optional[Any] = None,
|
|
48
|
+
max_iterations: Optional[int] = None,
|
|
48
49
|
) -> None:
|
|
49
50
|
"""
|
|
50
51
|
Initialize a new InvestigationAgent instance.
|
|
@@ -97,6 +98,7 @@ class InvestigationAgent(BaseAgent):
|
|
|
97
98
|
usage_recorder=usage_recorder,
|
|
98
99
|
sub_agent_recorder=sub_agent_recorder,
|
|
99
100
|
hook_dispatcher=hook_dispatcher,
|
|
101
|
+
max_iterations=max_iterations,
|
|
100
102
|
)
|
|
101
103
|
|
|
102
104
|
self.tool_collection = ToolCollection(
|
|
@@ -44,6 +44,7 @@ class PlanningAgent(BaseAgent):
|
|
|
44
44
|
usage_recorder: Optional[Any] = None,
|
|
45
45
|
sub_agent_recorder: Optional[Any] = None,
|
|
46
46
|
hook_dispatcher: Optional[Any] = None,
|
|
47
|
+
max_iterations: Optional[int] = None,
|
|
47
48
|
) -> None:
|
|
48
49
|
super().__init__(
|
|
49
50
|
project_path,
|
|
@@ -70,6 +71,7 @@ class PlanningAgent(BaseAgent):
|
|
|
70
71
|
usage_recorder=usage_recorder,
|
|
71
72
|
sub_agent_recorder=sub_agent_recorder,
|
|
72
73
|
hook_dispatcher=hook_dispatcher,
|
|
74
|
+
max_iterations=max_iterations,
|
|
73
75
|
)
|
|
74
76
|
|
|
75
77
|
self._completed_plan: Optional[str] = None
|
{kolega_code-0.8.0 → kolega_code-0.8.1}/kolega_code/agent/tests/llm/test_chatgpt_oauth_provider.py
RENAMED
|
@@ -13,6 +13,7 @@ from kolega_code.llm.models import (
|
|
|
13
13
|
ImageBlock,
|
|
14
14
|
Message,
|
|
15
15
|
MessageHistory,
|
|
16
|
+
ResponsesReasoningBlock,
|
|
16
17
|
TextBlock,
|
|
17
18
|
ToolCall,
|
|
18
19
|
ToolDefinition,
|
|
@@ -149,6 +150,32 @@ def test_to_responses_input_multiple_tool_outputs_before_image_followups():
|
|
|
149
150
|
assert any(part.get("type") == "input_image" for part in items[2]["content"])
|
|
150
151
|
|
|
151
152
|
|
|
153
|
+
def test_to_responses_input_resends_reasoning_before_tool_call():
|
|
154
|
+
# A prior assistant turn carrying captured reasoning must resend it as a
|
|
155
|
+
# reasoning item that *precedes* the function_call it belongs to.
|
|
156
|
+
history = MessageHistory(
|
|
157
|
+
[
|
|
158
|
+
Message(
|
|
159
|
+
role="assistant",
|
|
160
|
+
content=[
|
|
161
|
+
ResponsesReasoningBlock(encrypted_content="ENC", summary=["thinking..."], item_id="rs_1"),
|
|
162
|
+
ToolCall(id="call_1", name="read_file", input={"path": "a.py"}),
|
|
163
|
+
],
|
|
164
|
+
),
|
|
165
|
+
]
|
|
166
|
+
)
|
|
167
|
+
items = to_responses_input(history)
|
|
168
|
+
assert items[0] == {
|
|
169
|
+
"type": "reasoning",
|
|
170
|
+
"summary": [{"type": "summary_text", "text": "thinking..."}],
|
|
171
|
+
"encrypted_content": "ENC",
|
|
172
|
+
}
|
|
173
|
+
# The opaque server id is intentionally not resent (matches Codex, store=false).
|
|
174
|
+
assert "id" not in items[0]
|
|
175
|
+
assert items[1]["type"] == "function_call"
|
|
176
|
+
assert items[1]["call_id"] == "call_1"
|
|
177
|
+
|
|
178
|
+
|
|
152
179
|
def test_responses_tools_flattens_function_shape():
|
|
153
180
|
tool = ToolDefinition(
|
|
154
181
|
name="read_file",
|
|
@@ -231,6 +258,9 @@ async def test_responses_stream_wrapper_text_tools_and_usage():
|
|
|
231
258
|
assert tool_calls[0].input == {"path": "a.py"}
|
|
232
259
|
assert message.usage_metadata["prompt_tokens"] == 12
|
|
233
260
|
assert message.usage_metadata["completion_tokens"] == 4
|
|
261
|
+
# Messages must be tagged with the real provider so history adaptation treats
|
|
262
|
+
# them as openai_chatgpt (not the api-key "openai" provider).
|
|
263
|
+
assert message.usage_metadata["provider"] == "openai_chatgpt"
|
|
234
264
|
|
|
235
265
|
|
|
236
266
|
@pytest.mark.asyncio
|
|
@@ -313,6 +343,86 @@ async def test_responses_stream_wrapper_max_tokens_stop_reason():
|
|
|
313
343
|
assert message.stop_reason == "max_tokens"
|
|
314
344
|
|
|
315
345
|
|
|
346
|
+
@pytest.mark.asyncio
|
|
347
|
+
async def test_responses_stream_wrapper_captures_reasoning_for_continuity():
|
|
348
|
+
# With include=["reasoning.encrypted_content"], the backend emits a completed
|
|
349
|
+
# reasoning item carrying the encrypted blob. We capture it (leading the
|
|
350
|
+
# assistant message) so it round-trips into the next request for continuity.
|
|
351
|
+
completed = _ns(output=[], usage=None, status="completed", incomplete_details=None)
|
|
352
|
+
events = [
|
|
353
|
+
_ns(
|
|
354
|
+
type="response.output_item.done",
|
|
355
|
+
item=_ns(
|
|
356
|
+
type="reasoning",
|
|
357
|
+
id="rs_1",
|
|
358
|
+
encrypted_content="ENC",
|
|
359
|
+
summary=[_ns(type="summary_text", text="planning")],
|
|
360
|
+
),
|
|
361
|
+
),
|
|
362
|
+
_ns(
|
|
363
|
+
type="response.output_item.done",
|
|
364
|
+
item=_ns(type="function_call", id="fc_1", call_id="call_1", name="read_file", arguments='{"path": "a.py"}'),
|
|
365
|
+
),
|
|
366
|
+
_ns(type="response.completed", response=completed),
|
|
367
|
+
]
|
|
368
|
+
wrapper = ResponsesStreamWrapper(_FakeStream(events))
|
|
369
|
+
async with wrapper as stream:
|
|
370
|
+
async for _chunk in stream:
|
|
371
|
+
pass
|
|
372
|
+
message = await wrapper.get_final_message()
|
|
373
|
+
|
|
374
|
+
# Reasoning leads the assistant content so it precedes the tool call on resend.
|
|
375
|
+
assert isinstance(message.content[0], ResponsesReasoningBlock)
|
|
376
|
+
assert message.content[0].encrypted_content == "ENC"
|
|
377
|
+
assert message.content[0].summary == ["planning"]
|
|
378
|
+
|
|
379
|
+
items = to_responses_input(MessageHistory([message]))
|
|
380
|
+
assert items[0]["type"] == "reasoning"
|
|
381
|
+
assert items[0]["encrypted_content"] == "ENC"
|
|
382
|
+
assert items[1]["type"] == "function_call"
|
|
383
|
+
|
|
384
|
+
|
|
385
|
+
@pytest.mark.asyncio
|
|
386
|
+
async def test_responses_stream_wrapper_reasoning_from_final_output_fallback():
|
|
387
|
+
# Some backends populate reasoning only on the final response output, not via
|
|
388
|
+
# output_item.done events. The fallback path must still capture it.
|
|
389
|
+
completed = _ns(
|
|
390
|
+
output=[
|
|
391
|
+
_ns(type="reasoning", id="rs_1", encrypted_content="ENC2", summary=[]),
|
|
392
|
+
_ns(type="message", content=[_ns(type="output_text", text="hi")]),
|
|
393
|
+
],
|
|
394
|
+
usage=None,
|
|
395
|
+
status="completed",
|
|
396
|
+
incomplete_details=None,
|
|
397
|
+
)
|
|
398
|
+
wrapper = ResponsesStreamWrapper(_FakeStream([_ns(type="response.completed", response=completed)]))
|
|
399
|
+
async with wrapper as stream:
|
|
400
|
+
async for _chunk in stream:
|
|
401
|
+
pass
|
|
402
|
+
message = await wrapper.get_final_message()
|
|
403
|
+
reasoning = [b for b in message.content if isinstance(b, ResponsesReasoningBlock)]
|
|
404
|
+
assert reasoning and reasoning[0].encrypted_content == "ENC2"
|
|
405
|
+
assert reasoning[0].summary == []
|
|
406
|
+
|
|
407
|
+
|
|
408
|
+
@pytest.mark.asyncio
|
|
409
|
+
async def test_responses_stream_wrapper_skips_reasoning_without_encrypted_content():
|
|
410
|
+
# A reasoning item lacking encrypted_content is useless for continuity (and
|
|
411
|
+
# would be rejected on resend), so it must be dropped.
|
|
412
|
+
completed = _ns(output=[], usage=None, status="completed", incomplete_details=None)
|
|
413
|
+
events = [
|
|
414
|
+
_ns(type="response.output_item.done", item=_ns(type="reasoning", id="rs_1", encrypted_content=None, summary=[])),
|
|
415
|
+
_ns(type="response.output_text.delta", delta="ok"),
|
|
416
|
+
_ns(type="response.completed", response=completed),
|
|
417
|
+
]
|
|
418
|
+
wrapper = ResponsesStreamWrapper(_FakeStream(events))
|
|
419
|
+
async with wrapper as stream:
|
|
420
|
+
async for _chunk in stream:
|
|
421
|
+
pass
|
|
422
|
+
message = await wrapper.get_final_message()
|
|
423
|
+
assert not [b for b in message.content if isinstance(b, ResponsesReasoningBlock)]
|
|
424
|
+
|
|
425
|
+
|
|
316
426
|
# --- provider request building (no network) -------------------------------------
|
|
317
427
|
|
|
318
428
|
|
|
@@ -347,12 +457,18 @@ async def test_provider_generate_builds_codex_shaped_request():
|
|
|
347
457
|
)
|
|
348
458
|
|
|
349
459
|
assert message.get_text_content() == "hi"
|
|
460
|
+
assert message.usage_metadata["provider"] == "openai_chatgpt"
|
|
350
461
|
kwargs = fake.last_kwargs
|
|
351
462
|
assert kwargs["model"] == "gpt-5.5"
|
|
352
463
|
assert kwargs["store"] is False
|
|
353
464
|
assert kwargs["stream"] is True # backend is SSE-only; generate streams too
|
|
354
465
|
assert kwargs["instructions"] == "sys"
|
|
466
|
+
# summary="auto" so the backend streams a reasoning summary for the thinking
|
|
467
|
+
# display; this is independent of continuity, which rides on `include` below.
|
|
355
468
|
assert kwargs["reasoning"] == {"effort": "high", "summary": "auto"}
|
|
469
|
+
# Reasoning continuity: ask the backend for the encrypted reasoning blob so it
|
|
470
|
+
# can be resent next turn (matches Codex; the cause of the long-thinking gap).
|
|
471
|
+
assert kwargs["include"] == ["reasoning.encrypted_content"]
|
|
356
472
|
assert kwargs["tool_choice"] == "auto"
|
|
357
473
|
assert kwargs["parallel_tool_calls"] is False
|
|
358
474
|
assert "prompt_cache_key" in kwargs
|
|
@@ -361,6 +477,26 @@ async def test_provider_generate_builds_codex_shaped_request():
|
|
|
361
477
|
assert kwargs["input"] == [{"role": "user", "content": [{"type": "input_text", "text": "hello"}]}]
|
|
362
478
|
|
|
363
479
|
|
|
480
|
+
@pytest.mark.asyncio
|
|
481
|
+
async def test_provider_generate_omits_reasoning_and_include_without_thinking():
|
|
482
|
+
provider = ChatGPTOAuthProvider(token_manager=ChatGPTTokenManager(_tokens()))
|
|
483
|
+
completed = _ns(
|
|
484
|
+
output=[_ns(type="message", content=[_ns(type="output_text", text="hi")])],
|
|
485
|
+
usage=None,
|
|
486
|
+
status="completed",
|
|
487
|
+
incomplete_details=None,
|
|
488
|
+
)
|
|
489
|
+
fake = _FakeResponses(_FakeStream([_ns(type="response.completed", response=completed)]))
|
|
490
|
+
provider.async_client = _ns(responses=fake)
|
|
491
|
+
await provider.generate(
|
|
492
|
+
MessageHistory([Message(role="user", content=[TextBlock(text="hello")])]),
|
|
493
|
+
params=GenerationParams(), # no thinking effort
|
|
494
|
+
model="gpt-5.5",
|
|
495
|
+
)
|
|
496
|
+
assert "reasoning" not in fake.last_kwargs
|
|
497
|
+
assert "include" not in fake.last_kwargs
|
|
498
|
+
|
|
499
|
+
|
|
364
500
|
@pytest.mark.asyncio
|
|
365
501
|
async def test_provider_stream_returns_wrapper():
|
|
366
502
|
provider = ChatGPTOAuthProvider(token_manager=ChatGPTTokenManager(_tokens()))
|
|
@@ -318,8 +318,9 @@ class TestProviderInitialization:
|
|
|
318
318
|
|
|
319
319
|
def test_provider_initialization_with_error(self):
|
|
320
320
|
"""Test that provider initialization errors are properly wrapped."""
|
|
321
|
-
#
|
|
322
|
-
|
|
321
|
+
# The api-key `openai` provider now uses the Responses provider, so mock
|
|
322
|
+
# that class to raise during initialization.
|
|
323
|
+
with patch("kolega_code.llm.client.OpenAIResponsesProvider") as MockProvider:
|
|
323
324
|
MockProvider.side_effect = RuntimeError("Provider init failed")
|
|
324
325
|
|
|
325
326
|
with pytest.raises(LLMInternalServerError) as exc_info:
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
"""Tests for the api-key OpenAI Responses provider and its routing.
|
|
2
|
+
|
|
3
|
+
gpt-5.x reject ``function tools + reasoning_effort`` on Chat Completions, so the
|
|
4
|
+
api-key ``openai`` provider routes to the Responses API. The OpenAI-compatible
|
|
5
|
+
providers (fireworks, xai, …) keep using the Chat Completions ``OpenAIProvider``.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
import types
|
|
9
|
+
|
|
10
|
+
import pytest
|
|
11
|
+
|
|
12
|
+
from kolega_code.llm.client import LLMClient
|
|
13
|
+
from kolega_code.llm.models import (
|
|
14
|
+
Message,
|
|
15
|
+
MessageHistory,
|
|
16
|
+
ResponsesReasoningBlock,
|
|
17
|
+
TextBlock,
|
|
18
|
+
ToolDefinition,
|
|
19
|
+
ToolParameter,
|
|
20
|
+
)
|
|
21
|
+
from kolega_code.llm.providers.models import GenerationParams
|
|
22
|
+
from kolega_code.llm.providers.openai import OpenAIProvider
|
|
23
|
+
from kolega_code.llm.providers.openai_responses import OpenAIResponsesProvider
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def _ns(**kwargs):
|
|
27
|
+
return types.SimpleNamespace(**kwargs)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class _FakeStream:
|
|
31
|
+
def __init__(self, events):
|
|
32
|
+
self._events = events
|
|
33
|
+
|
|
34
|
+
def __aiter__(self):
|
|
35
|
+
async def gen():
|
|
36
|
+
for event in self._events:
|
|
37
|
+
yield event
|
|
38
|
+
|
|
39
|
+
return gen()
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class _FakeResponses:
|
|
43
|
+
def __init__(self, result):
|
|
44
|
+
self._result = result
|
|
45
|
+
self.last_kwargs = None
|
|
46
|
+
|
|
47
|
+
async def create(self, **kwargs):
|
|
48
|
+
self.last_kwargs = kwargs
|
|
49
|
+
return self._result
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
# --- routing --------------------------------------------------------------------
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def test_llmclient_routes_openai_to_responses_provider():
|
|
56
|
+
client = LLMClient(provider="openai", api_key="sk-test")
|
|
57
|
+
assert isinstance(client.provider, OpenAIResponsesProvider)
|
|
58
|
+
assert client.provider.provider_name == "openai"
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def test_llmclient_routes_compatible_provider_to_chat_completions():
|
|
62
|
+
client = LLMClient(provider="fireworks", api_key="sk-test")
|
|
63
|
+
assert isinstance(client.provider, OpenAIProvider)
|
|
64
|
+
assert not isinstance(client.provider, OpenAIResponsesProvider)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# --- request building (no network) ----------------------------------------------
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_build_request_is_responses_shaped_with_reasoning():
|
|
71
|
+
provider = OpenAIResponsesProvider(api_key="sk-test")
|
|
72
|
+
tool = ToolDefinition(
|
|
73
|
+
name="read_file",
|
|
74
|
+
description="Read a file",
|
|
75
|
+
parameters=[ToolParameter(name="path", type="string", description="path", required=True)],
|
|
76
|
+
)
|
|
77
|
+
params = GenerationParams(tools=[tool], thinking="high", max_completion_tokens=256, temperature=0.5)
|
|
78
|
+
request = provider._build_request(
|
|
79
|
+
MessageHistory([Message(role="user", content=[TextBlock(text="hello")])]),
|
|
80
|
+
Message(role="system", content=[TextBlock(text="sys")]),
|
|
81
|
+
params,
|
|
82
|
+
{"model": "gpt-5.5"},
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
assert request["model"] == "gpt-5.5"
|
|
86
|
+
assert request["stream"] is True
|
|
87
|
+
assert request["store"] is False
|
|
88
|
+
assert request["tool_choice"] == "auto"
|
|
89
|
+
assert request["instructions"] == "sys"
|
|
90
|
+
assert request["reasoning"] == {"effort": "high", "summary": "auto"}
|
|
91
|
+
assert request["include"] == ["reasoning.encrypted_content"]
|
|
92
|
+
assert request["tools"][0]["type"] == "function"
|
|
93
|
+
assert request["tools"][0]["name"] == "read_file"
|
|
94
|
+
assert request["input"] == [{"role": "user", "content": [{"type": "input_text", "text": "hello"}]}]
|
|
95
|
+
# The Responses path must NOT send Chat-Completions-only fields.
|
|
96
|
+
assert "temperature" not in request
|
|
97
|
+
assert "max_completion_tokens" not in request
|
|
98
|
+
assert "max_output_tokens" not in request
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def test_build_request_default_model_and_no_reasoning_without_thinking():
|
|
102
|
+
provider = OpenAIResponsesProvider(api_key="sk-test")
|
|
103
|
+
request = provider._build_request(
|
|
104
|
+
MessageHistory([Message(role="user", content=[TextBlock(text="hi")])]),
|
|
105
|
+
None,
|
|
106
|
+
GenerationParams(),
|
|
107
|
+
{},
|
|
108
|
+
)
|
|
109
|
+
assert request["model"] == "gpt-5.5"
|
|
110
|
+
assert "reasoning" not in request
|
|
111
|
+
assert "include" not in request
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
# --- generate / stream (no network) ---------------------------------------------
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
@pytest.mark.asyncio
|
|
118
|
+
async def test_generate_tags_openai_provider_and_sends_include():
|
|
119
|
+
provider = OpenAIResponsesProvider(api_key="sk-test")
|
|
120
|
+
completed = _ns(
|
|
121
|
+
output=[_ns(type="message", content=[_ns(type="output_text", text="hi")])],
|
|
122
|
+
usage=_ns(input_tokens=3, output_tokens=2, total_tokens=5, input_tokens_details=None),
|
|
123
|
+
status="completed",
|
|
124
|
+
incomplete_details=None,
|
|
125
|
+
)
|
|
126
|
+
fake = _FakeResponses(_FakeStream([_ns(type="response.completed", response=completed)]))
|
|
127
|
+
provider.async_client = _ns(responses=fake)
|
|
128
|
+
|
|
129
|
+
msg = await provider.generate(
|
|
130
|
+
MessageHistory([Message(role="user", content=[TextBlock(text="hello")])]),
|
|
131
|
+
params=GenerationParams(thinking="medium"),
|
|
132
|
+
model="gpt-5.5",
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
assert msg.get_text_content() == "hi"
|
|
136
|
+
assert msg.usage_metadata["provider"] == "openai"
|
|
137
|
+
assert fake.last_kwargs["include"] == ["reasoning.encrypted_content"]
|
|
138
|
+
assert fake.last_kwargs["reasoning"] == {"effort": "medium", "summary": "auto"}
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
@pytest.mark.asyncio
|
|
142
|
+
async def test_stream_captures_reasoning_for_continuity():
|
|
143
|
+
provider = OpenAIResponsesProvider(api_key="sk-test")
|
|
144
|
+
completed = _ns(output=[], usage=None, status="completed", incomplete_details=None)
|
|
145
|
+
events = [
|
|
146
|
+
_ns(type="response.output_item.done", item=_ns(type="reasoning", id="rs_1", encrypted_content="ENC", summary=[])),
|
|
147
|
+
_ns(type="response.output_text.delta", delta="hi"),
|
|
148
|
+
_ns(type="response.completed", response=completed),
|
|
149
|
+
]
|
|
150
|
+
provider.async_client = _ns(responses=_FakeResponses(_FakeStream(events)))
|
|
151
|
+
|
|
152
|
+
wrapper = await provider.stream(
|
|
153
|
+
MessageHistory([Message(role="user", content=[TextBlock(text="x")])]),
|
|
154
|
+
params=GenerationParams(thinking="high"),
|
|
155
|
+
model="gpt-5.5",
|
|
156
|
+
)
|
|
157
|
+
async with wrapper as stream:
|
|
158
|
+
async for _chunk in stream:
|
|
159
|
+
pass
|
|
160
|
+
message = await wrapper.get_final_message()
|
|
161
|
+
|
|
162
|
+
assert isinstance(message.content[0], ResponsesReasoningBlock)
|
|
163
|
+
assert message.content[0].encrypted_content == "ENC"
|
|
@@ -149,13 +149,17 @@ def test_google_gemini_3_pro_uses_thinking_level() -> None:
|
|
|
149
149
|
|
|
150
150
|
|
|
151
151
|
def test_openai_reasoning_effort_is_sent_for_reasoning_models() -> None:
|
|
152
|
+
# The api-key `openai` provider's gpt-5.x models now use the Responses API
|
|
153
|
+
# (served by OpenAIResponsesProvider), so effort serializes to a nested
|
|
154
|
+
# reasoning object rather than a flat reasoning_effort field.
|
|
152
155
|
provider = OpenAIProvider(api_key="test-key")
|
|
153
156
|
generation_params = provider._prepare_generation_params(GenerationParams(thinking="high"))
|
|
154
157
|
generation_params["model"] = "gpt-5.5"
|
|
155
158
|
|
|
156
159
|
provider._apply_thinking_params(generation_params, GenerationParams(thinking="high"))
|
|
157
160
|
|
|
158
|
-
assert generation_params["
|
|
161
|
+
assert generation_params["reasoning"] == {"effort": "high", "summary": "auto"}
|
|
162
|
+
assert "reasoning_effort" not in generation_params
|
|
159
163
|
|
|
160
164
|
|
|
161
165
|
def test_numeric_thinking_budget_is_rejected() -> None:
|