free-claude-code 6.2.64__py3-none-any.whl
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.
- free_claude_code/__init__.py +1 -0
- free_claude_code/api/__init__.py +1 -0
- free_claude_code/api/admin_cache.py +49 -0
- free_claude_code/api/admin_routes.py +501 -0
- free_claude_code/api/admin_security.py +67 -0
- free_claude_code/api/admin_static/admin.css +980 -0
- free_claude_code/api/admin_static/admin.js +1934 -0
- free_claude_code/api/admin_static/code_sessions.css +109 -0
- free_claude_code/api/admin_static/code_sessions.js +1693 -0
- free_claude_code/api/admin_static/form_controls.js +15 -0
- free_claude_code/api/admin_static/index.html +201 -0
- free_claude_code/api/admin_static/model_combobox.js +194 -0
- free_claude_code/api/admin_static/providers/LICENSE-llamacpp.txt +21 -0
- free_claude_code/api/admin_static/providers/LICENSE-lobe-icons.txt +21 -0
- free_claude_code/api/admin_static/providers/SOURCES.md +56 -0
- free_claude_code/api/admin_static/providers/agnesai.svg +1 -0
- free_claude_code/api/admin_static/providers/azure-color.svg +1 -0
- free_claude_code/api/admin_static/providers/bedrock-color.svg +1 -0
- free_claude_code/api/admin_static/providers/cerebras-color.svg +1 -0
- free_claude_code/api/admin_static/providers/chutes.png +0 -0
- free_claude_code/api/admin_static/providers/cline.svg +1 -0
- free_claude_code/api/admin_static/providers/cloudflare-color.svg +1 -0
- free_claude_code/api/admin_static/providers/cohere-color.svg +1 -0
- free_claude_code/api/admin_static/providers/deepinfra-color.svg +1 -0
- free_claude_code/api/admin_static/providers/deepseek-color.svg +1 -0
- free_claude_code/api/admin_static/providers/experiential.svg +9 -0
- free_claude_code/api/admin_static/providers/featherless-color.svg +1 -0
- free_claude_code/api/admin_static/providers/fireworks-color.svg +1 -0
- free_claude_code/api/admin_static/providers/gemini-color.svg +1 -0
- free_claude_code/api/admin_static/providers/githubcopilot.svg +1 -0
- free_claude_code/api/admin_static/providers/groq.svg +1 -0
- free_claude_code/api/admin_static/providers/huggingface-color.svg +1 -0
- free_claude_code/api/admin_static/providers/kilocode.svg +1 -0
- free_claude_code/api/admin_static/providers/kimi-color.svg +1 -0
- free_claude_code/api/admin_static/providers/lightning.png +0 -0
- free_claude_code/api/admin_static/providers/llamacpp.svg +77 -0
- free_claude_code/api/admin_static/providers/llm7.png +0 -0
- free_claude_code/api/admin_static/providers/lmstudio.svg +1 -0
- free_claude_code/api/admin_static/providers/minimax-color.svg +1 -0
- free_claude_code/api/admin_static/providers/mistral-color.svg +1 -0
- free_claude_code/api/admin_static/providers/nararoute.png +0 -0
- free_claude_code/api/admin_static/providers/nebius.svg +1 -0
- free_claude_code/api/admin_static/providers/novita-color.svg +1 -0
- free_claude_code/api/admin_static/providers/nvidia-color.svg +1 -0
- free_claude_code/api/admin_static/providers/ollama.svg +1 -0
- free_claude_code/api/admin_static/providers/openai.svg +1 -0
- free_claude_code/api/admin_static/providers/opencode.svg +1 -0
- free_claude_code/api/admin_static/providers/openrouter.svg +1 -0
- free_claude_code/api/admin_static/providers/poolside-color.svg +1 -0
- free_claude_code/api/admin_static/providers/qwen-color.svg +1 -0
- free_claude_code/api/admin_static/providers/sambanova-color.svg +1 -0
- free_claude_code/api/admin_static/providers/scaleway.svg +11 -0
- free_claude_code/api/admin_static/providers/siliconcloud-color.svg +1 -0
- free_claude_code/api/admin_static/providers/together-color.svg +1 -0
- free_claude_code/api/admin_static/providers/tokenrouter.png +0 -0
- free_claude_code/api/admin_static/providers/vercel.svg +1 -0
- free_claude_code/api/admin_static/providers/vertexai-color.svg +1 -0
- free_claude_code/api/admin_static/providers/wafer.png +0 -0
- free_claude_code/api/admin_static/providers/wandb.png +0 -0
- free_claude_code/api/admin_static/providers/xai.svg +1 -0
- free_claude_code/api/admin_static/providers/zai.svg +1 -0
- free_claude_code/api/admin_static/providers/zenmux.svg +1 -0
- free_claude_code/api/admin_static/session_layout.css +552 -0
- free_claude_code/api/admin_static/session_ui.js +202 -0
- free_claude_code/api/app.py +149 -0
- free_claude_code/api/code_sessions_routes.py +386 -0
- free_claude_code/api/command_utils.py +164 -0
- free_claude_code/api/dependencies.py +128 -0
- free_claude_code/api/detection.py +185 -0
- free_claude_code/api/handlers/__init__.py +7 -0
- free_claude_code/api/handlers/classifier_response.py +66 -0
- free_claude_code/api/handlers/messages.py +359 -0
- free_claude_code/api/handlers/responses.py +163 -0
- free_claude_code/api/handlers/token_count.py +87 -0
- free_claude_code/api/markdown.py +71 -0
- free_claude_code/api/model_catalog.py +282 -0
- free_claude_code/api/optimization_handlers.py +162 -0
- free_claude_code/api/ports.py +97 -0
- free_claude_code/api/request_errors.py +100 -0
- free_claude_code/api/request_ids.py +100 -0
- free_claude_code/api/request_lifetime.py +130 -0
- free_claude_code/api/response_streams.py +422 -0
- free_claude_code/api/routes.py +269 -0
- free_claude_code/api/validation_log.py +46 -0
- free_claude_code/application/__init__.py +1 -0
- free_claude_code/application/code_sessions/__init__.py +33 -0
- free_claude_code/application/code_sessions/models.py +227 -0
- free_claude_code/application/code_sessions/ports.py +197 -0
- free_claude_code/application/code_sessions/service.py +1123 -0
- free_claude_code/application/code_sessions/state.py +659 -0
- free_claude_code/application/connected_accounts.py +96 -0
- free_claude_code/application/errors.py +41 -0
- free_claude_code/application/execution.py +467 -0
- free_claude_code/application/model_catalog.py +95 -0
- free_claude_code/application/model_metadata.py +26 -0
- free_claude_code/application/ports.py +127 -0
- free_claude_code/application/readiness.py +43 -0
- free_claude_code/application/reasoning.py +134 -0
- free_claude_code/application/routing.py +291 -0
- free_claude_code/application/session_events.py +140 -0
- free_claude_code/application/web_tools/__init__.py +1 -0
- free_claude_code/application/web_tools/ports.py +35 -0
- free_claude_code/application/web_tools/request.py +348 -0
- free_claude_code/application/web_tools/service.py +481 -0
- free_claude_code/assets/app-icon.icns +0 -0
- free_claude_code/assets/app-icon.ico +0 -0
- free_claude_code/assets/app-icon.png +0 -0
- free_claude_code/assets/app-icon.svg +14 -0
- free_claude_code/cli/__init__.py +5 -0
- free_claude_code/cli/commands.py +379 -0
- free_claude_code/cli/desktop.py +156 -0
- free_claude_code/cli/desktop_assets.py +31 -0
- free_claude_code/cli/desktop_entrypoint.py +29 -0
- free_claude_code/cli/desktop_tray.py +67 -0
- free_claude_code/cli/entrypoints.py +25 -0
- free_claude_code/cli/launchers/__init__.py +1 -0
- free_claude_code/cli/launchers/aider.py +53 -0
- free_claude_code/cli/launchers/aider_config.py +80 -0
- free_claude_code/cli/launchers/catalog_http.py +134 -0
- free_claude_code/cli/launchers/claude.py +34 -0
- free_claude_code/cli/launchers/cline.py +60 -0
- free_claude_code/cli/launchers/cline_config.py +121 -0
- free_claude_code/cli/launchers/codex.py +47 -0
- free_claude_code/cli/launchers/common.py +103 -0
- free_claude_code/cli/launchers/dsh.py +69 -0
- free_claude_code/cli/launchers/dsh_config.py +124 -0
- free_claude_code/cli/launchers/grok.py +116 -0
- free_claude_code/cli/launchers/hermes.py +85 -0
- free_claude_code/cli/launchers/hermes_config.py +134 -0
- free_claude_code/cli/launchers/muse.py +68 -0
- free_claude_code/cli/launchers/opencode.py +83 -0
- free_claude_code/cli/launchers/opencode_config.py +107 -0
- free_claude_code/cli/launchers/pi.py +70 -0
- free_claude_code/cli/launchers/pi_extension.ts +190 -0
- free_claude_code/cli/launchers/runner.py +155 -0
- free_claude_code/cli/local_http.py +12 -0
- free_claude_code/cli/managed/__init__.py +6 -0
- free_claude_code/cli/managed/claude.py +225 -0
- free_claude_code/cli/managed/diagnostics.py +47 -0
- free_claude_code/cli/managed/manager.py +207 -0
- free_claude_code/cli/managed/session.py +293 -0
- free_claude_code/cli/process_registry.py +76 -0
- free_claude_code/cli/server_socket.py +71 -0
- free_claude_code/cli/uvicorn_server.py +41 -0
- free_claude_code/config/__init__.py +10 -0
- free_claude_code/config/admin/__init__.py +1 -0
- free_claude_code/config/admin/manifest.py +610 -0
- free_claude_code/config/admin/persistence.py +202 -0
- free_claude_code/config/admin/provider_manifest.py +443 -0
- free_claude_code/config/admin/specs.py +77 -0
- free_claude_code/config/admin/state.py +18 -0
- free_claude_code/config/admin/status.py +116 -0
- free_claude_code/config/admin/validation.py +40 -0
- free_claude_code/config/admin/values.py +115 -0
- free_claude_code/config/constants.py +9 -0
- free_claude_code/config/env_files.py +74 -0
- free_claude_code/config/env_migrations.py +457 -0
- free_claude_code/config/loader.py +196 -0
- free_claude_code/config/logging_config.py +197 -0
- free_claude_code/config/model_refs.py +118 -0
- free_claude_code/config/nim.py +121 -0
- free_claude_code/config/paths.py +104 -0
- free_claude_code/config/provider_catalog.py +746 -0
- free_claude_code/config/provider_proxies.py +44 -0
- free_claude_code/config/reasoning.py +24 -0
- free_claude_code/config/server_urls.py +55 -0
- free_claude_code/config/settings.py +812 -0
- free_claude_code/core/__init__.py +15 -0
- free_claude_code/core/anthropic/__init__.py +99 -0
- free_claude_code/core/anthropic/content.py +31 -0
- free_claude_code/core/anthropic/conversion.py +876 -0
- free_claude_code/core/anthropic/errors.py +97 -0
- free_claude_code/core/anthropic/image_sources.py +77 -0
- free_claude_code/core/anthropic/models.py +193 -0
- free_claude_code/core/anthropic/native.py +184 -0
- free_claude_code/core/anthropic/native_stream.py +306 -0
- free_claude_code/core/anthropic/request_serialization.py +57 -0
- free_claude_code/core/anthropic/request_snapshot.py +35 -0
- free_claude_code/core/anthropic/server_tool_sse.py +234 -0
- free_claude_code/core/anthropic/server_tool_types.py +9 -0
- free_claude_code/core/anthropic/sse_aggregation.py +144 -0
- free_claude_code/core/anthropic/stream_contracts.py +208 -0
- free_claude_code/core/anthropic/streaming/__init__.py +43 -0
- free_claude_code/core/anthropic/streaming/decoder.py +49 -0
- free_claude_code/core/anthropic/streaming/emitter.py +62 -0
- free_claude_code/core/anthropic/streaming/ledger.py +538 -0
- free_claude_code/core/anthropic/streaming/recovery.py +211 -0
- free_claude_code/core/anthropic/thinking.py +140 -0
- free_claude_code/core/anthropic/tokens.py +111 -0
- free_claude_code/core/anthropic/tool_results.py +63 -0
- free_claude_code/core/anthropic/tool_schema.py +94 -0
- free_claude_code/core/anthropic/tools.py +493 -0
- free_claude_code/core/anthropic/usage.py +38 -0
- free_claude_code/core/anthropic/utils.py +9 -0
- free_claude_code/core/async_iterators.py +26 -0
- free_claude_code/core/async_tasks.py +26 -0
- free_claude_code/core/data/README.md +12 -0
- free_claude_code/core/data/TIKTOKEN-LICENSE +21 -0
- free_claude_code/core/data/cl100k_base.tiktoken +100256 -0
- free_claude_code/core/diagnostics.py +319 -0
- free_claude_code/core/failures.py +50 -0
- free_claude_code/core/gateway_model_ids.py +73 -0
- free_claude_code/core/history_replay.py +561 -0
- free_claude_code/core/interprocess_lock.py +90 -0
- free_claude_code/core/json_types.py +7 -0
- free_claude_code/core/model_capabilities.py +10 -0
- free_claude_code/core/openai_chat.py +54 -0
- free_claude_code/core/openai_responses/__init__.py +90 -0
- free_claude_code/core/openai_responses/chat_request.py +747 -0
- free_claude_code/core/openai_responses/errors.py +78 -0
- free_claude_code/core/openai_responses/events.py +82 -0
- free_claude_code/core/openai_responses/ids.py +32 -0
- free_claude_code/core/openai_responses/items.py +35 -0
- free_claude_code/core/openai_responses/messages_request.py +624 -0
- free_claude_code/core/openai_responses/messages_stream.py +404 -0
- free_claude_code/core/openai_responses/models.py +33 -0
- free_claude_code/core/openai_responses/native.py +147 -0
- free_claude_code/core/openai_responses/provider_input.py +409 -0
- free_claude_code/core/openai_responses/provider_stream.py +282 -0
- free_claude_code/core/openai_responses/reasoning.py +73 -0
- free_claude_code/core/openai_responses/streaming/__init__.py +1 -0
- free_claude_code/core/openai_responses/streaming/blocks.py +36 -0
- free_claude_code/core/openai_responses/streaming/completion.py +169 -0
- free_claude_code/core/openai_responses/streaming/error_mapping.py +28 -0
- free_claude_code/core/openai_responses/streaming/event_builders.py +198 -0
- free_claude_code/core/openai_responses/streaming/ledger.py +96 -0
- free_claude_code/core/openai_responses/tokens.py +133 -0
- free_claude_code/core/openai_responses/tool_adaptation.py +828 -0
- free_claude_code/core/openai_responses/tool_search.py +204 -0
- free_claude_code/core/openai_responses/tools.py +169 -0
- free_claude_code/core/openai_tool_names.py +178 -0
- free_claude_code/core/rate_limit.py +72 -0
- free_claude_code/core/reasoning.py +136 -0
- free_claude_code/core/token_estimation.py +76 -0
- free_claude_code/core/tool_schema_patterns.py +168 -0
- free_claude_code/core/trace.py +197 -0
- free_claude_code/core/version.py +15 -0
- free_claude_code/core/web_tools.py +17 -0
- free_claude_code/core/windows_dpi.py +27 -0
- free_claude_code/harnesses/__init__.py +1 -0
- free_claude_code/harnesses/claude.py +42 -0
- free_claude_code/harnesses/claude_desktop_integration.py +418 -0
- free_claude_code/harnesses/claude_integration.py +167 -0
- free_claude_code/harnesses/codex.py +94 -0
- free_claude_code/harnesses/codex_integration.py +147 -0
- free_claude_code/harnesses/codex_model_catalog.py +109 -0
- free_claude_code/harnesses/config_file.py +33 -0
- free_claude_code/harnesses/environment.py +78 -0
- free_claude_code/harnesses/jetbrains_acp_integration.py +242 -0
- free_claude_code/harnesses/launch.py +21 -0
- free_claude_code/harnesses/resources.py +42 -0
- free_claude_code/messaging/__init__.py +16 -0
- free_claude_code/messaging/cli_event_constants.py +67 -0
- free_claude_code/messaging/command_context.py +99 -0
- free_claude_code/messaging/command_dispatcher.py +31 -0
- free_claude_code/messaging/commands.py +182 -0
- free_claude_code/messaging/event_parser.py +184 -0
- free_claude_code/messaging/limiter.py +342 -0
- free_claude_code/messaging/managed_protocols.py +35 -0
- free_claude_code/messaging/models.py +57 -0
- free_claude_code/messaging/node_event_pipeline.py +120 -0
- free_claude_code/messaging/node_runner.py +408 -0
- free_claude_code/messaging/platforms/__init__.py +20 -0
- free_claude_code/messaging/platforms/discord.py +319 -0
- free_claude_code/messaging/platforms/discord_inbound.py +112 -0
- free_claude_code/messaging/platforms/discord_io.py +167 -0
- free_claude_code/messaging/platforms/factory.py +109 -0
- free_claude_code/messaging/platforms/outbox.py +143 -0
- free_claude_code/messaging/platforms/ports.py +99 -0
- free_claude_code/messaging/platforms/telegram.py +300 -0
- free_claude_code/messaging/platforms/telegram_inbound.py +133 -0
- free_claude_code/messaging/platforms/telegram_io.py +287 -0
- free_claude_code/messaging/platforms/voice_flow.py +388 -0
- free_claude_code/messaging/rendering/__init__.py +3 -0
- free_claude_code/messaging/rendering/discord_markdown.py +318 -0
- free_claude_code/messaging/rendering/markdown_tables.py +47 -0
- free_claude_code/messaging/rendering/profiles.py +53 -0
- free_claude_code/messaging/rendering/telegram_markdown.py +331 -0
- free_claude_code/messaging/safe_diagnostics.py +15 -0
- free_claude_code/messaging/session/__init__.py +5 -0
- free_claude_code/messaging/session/managed_message_log.py +135 -0
- free_claude_code/messaging/session/persistence.py +161 -0
- free_claude_code/messaging/session/store.py +162 -0
- free_claude_code/messaging/transcript/__init__.py +6 -0
- free_claude_code/messaging/transcript/buffer.py +220 -0
- free_claude_code/messaging/transcript/context.py +18 -0
- free_claude_code/messaging/transcript/renderer.py +65 -0
- free_claude_code/messaging/transcript/segments.py +176 -0
- free_claude_code/messaging/transcript/subagents.py +108 -0
- free_claude_code/messaging/transcription.py +132 -0
- free_claude_code/messaging/trees/__init__.py +41 -0
- free_claude_code/messaging/trees/graph.py +250 -0
- free_claude_code/messaging/trees/identity.py +16 -0
- free_claude_code/messaging/trees/manager.py +587 -0
- free_claude_code/messaging/trees/node.py +56 -0
- free_claude_code/messaging/trees/processor.py +272 -0
- free_claude_code/messaging/trees/queue.py +47 -0
- free_claude_code/messaging/trees/repository.py +135 -0
- free_claude_code/messaging/trees/runtime.py +487 -0
- free_claude_code/messaging/trees/snapshot.py +221 -0
- free_claude_code/messaging/trees/transitions.py +174 -0
- free_claude_code/messaging/turn_intake.py +241 -0
- free_claude_code/messaging/ui_updates.py +99 -0
- free_claude_code/messaging/voice.py +376 -0
- free_claude_code/messaging/workflow.py +714 -0
- free_claude_code/providers/__init__.py +12 -0
- free_claude_code/providers/admission.py +942 -0
- free_claude_code/providers/anthropic_messages/__init__.py +1 -0
- free_claude_code/providers/anthropic_messages/request_policy.py +162 -0
- free_claude_code/providers/anthropic_messages/transport.py +499 -0
- free_claude_code/providers/base.py +73 -0
- free_claude_code/providers/cloudflare/__init__.py +8 -0
- free_claude_code/providers/cloudflare/client.py +166 -0
- free_claude_code/providers/credential_validation.py +314 -0
- free_claude_code/providers/deepseek/__init__.py +5 -0
- free_claude_code/providers/deepseek/client.py +117 -0
- free_claude_code/providers/deepseek/compat.py +241 -0
- free_claude_code/providers/endpoint.py +21 -0
- free_claude_code/providers/endpoint_types.py +21 -0
- free_claude_code/providers/failure_policy.py +574 -0
- free_claude_code/providers/gemini/__init__.py +5 -0
- free_claude_code/providers/gemini/client.py +42 -0
- free_claude_code/providers/github_copilot/__init__.py +1 -0
- free_claude_code/providers/github_copilot/auth.py +502 -0
- free_claude_code/providers/github_copilot/broker.py +248 -0
- free_claude_code/providers/github_copilot/lifecycle.py +18 -0
- free_claude_code/providers/github_copilot/login.py +112 -0
- free_claude_code/providers/github_copilot/native_cli.py +82 -0
- free_claude_code/providers/github_copilot/provider.py +367 -0
- free_claude_code/providers/github_copilot/request_policy.py +119 -0
- free_claude_code/providers/github_copilot/responses_events.py +90 -0
- free_claude_code/providers/github_copilot/sdk.py +496 -0
- free_claude_code/providers/github_copilot/types.py +67 -0
- free_claude_code/providers/google_openai/__init__.py +17 -0
- free_claude_code/providers/google_openai/provider.py +73 -0
- free_claude_code/providers/google_openai/reasoning.py +150 -0
- free_claude_code/providers/google_openai/thought_signatures.py +117 -0
- free_claude_code/providers/groq/__init__.py +5 -0
- free_claude_code/providers/groq/client.py +342 -0
- free_claude_code/providers/groq/tpm.py +114 -0
- free_claude_code/providers/history_replay.py +260 -0
- free_claude_code/providers/http.py +105 -0
- free_claude_code/providers/kilo/__init__.py +5 -0
- free_claude_code/providers/kilo/client.py +45 -0
- free_claude_code/providers/kilo/models.py +60 -0
- free_claude_code/providers/lmstudio/__init__.py +5 -0
- free_claude_code/providers/lmstudio/client.py +220 -0
- free_claude_code/providers/mistral/__init__.py +5 -0
- free_claude_code/providers/mistral/client.py +91 -0
- free_claude_code/providers/mistral/reasoning.py +410 -0
- free_claude_code/providers/model_listing.py +479 -0
- free_claude_code/providers/nvidia_nim/__init__.py +5 -0
- free_claude_code/providers/nvidia_nim/client.py +224 -0
- free_claude_code/providers/nvidia_nim/native_tool_stream.py +693 -0
- free_claude_code/providers/nvidia_nim/request_options.py +141 -0
- free_claude_code/providers/nvidia_nim/retry.py +85 -0
- free_claude_code/providers/nvidia_nim/tool_schema.py +255 -0
- free_claude_code/providers/nvidia_nim/voice.py +107 -0
- free_claude_code/providers/open_router/__init__.py +5 -0
- free_claude_code/providers/open_router/client.py +113 -0
- free_claude_code/providers/openai_api/__init__.py +5 -0
- free_claude_code/providers/openai_api/provider.py +150 -0
- free_claude_code/providers/openai_chat/__init__.py +72 -0
- free_claude_code/providers/openai_chat/base_url.py +7 -0
- free_claude_code/providers/openai_chat/behavior.py +133 -0
- free_claude_code/providers/openai_chat/client.py +40 -0
- free_claude_code/providers/openai_chat/extra_body.py +63 -0
- free_claude_code/providers/openai_chat/output_cap.py +161 -0
- free_claude_code/providers/openai_chat/profiles.py +778 -0
- free_claude_code/providers/openai_chat/provider.py +244 -0
- free_claude_code/providers/openai_chat/reasoning.py +169 -0
- free_claude_code/providers/openai_chat/reasoning_details.py +128 -0
- free_claude_code/providers/openai_chat/request_policy.py +140 -0
- free_claude_code/providers/openai_chat/stream_output.py +752 -0
- free_claude_code/providers/openai_chat/tool_calls.py +462 -0
- free_claude_code/providers/openai_chat/transport.py +1643 -0
- free_claude_code/providers/openai_chat/usage.py +117 -0
- free_claude_code/providers/openai_client.py +74 -0
- free_claude_code/providers/openai_codex/__init__.py +1 -0
- free_claude_code/providers/openai_codex/auth.py +626 -0
- free_claude_code/providers/openai_codex/endpoint.py +53 -0
- free_claude_code/providers/openai_codex/login.py +265 -0
- free_claude_code/providers/openai_codex/provider.py +259 -0
- free_claude_code/providers/openai_responses/__init__.py +5 -0
- free_claude_code/providers/openai_responses/presentation.py +115 -0
- free_claude_code/providers/openai_responses/transport.py +621 -0
- free_claude_code/providers/openai_stream.py +21 -0
- free_claude_code/providers/opencode/__init__.py +5 -0
- free_claude_code/providers/opencode/catalog.py +340 -0
- free_claude_code/providers/opencode/provider.py +359 -0
- free_claude_code/providers/reasoning_compatibility.py +199 -0
- free_claude_code/providers/request_recovery.py +122 -0
- free_claude_code/providers/runtime/__init__.py +10 -0
- free_claude_code/providers/runtime/config.py +91 -0
- free_claude_code/providers/runtime/discovery.py +63 -0
- free_claude_code/providers/runtime/factory.py +282 -0
- free_claude_code/providers/runtime/model_cache.py +93 -0
- free_claude_code/providers/runtime/runtime.py +108 -0
- free_claude_code/providers/stream_recovery.py +164 -0
- free_claude_code/providers/vertex/__init__.py +5 -0
- free_claude_code/providers/vertex/auth.py +105 -0
- free_claude_code/providers/vertex/client.py +140 -0
- free_claude_code/providers/vertex/endpoint.py +50 -0
- free_claude_code/providers/vertex/models.py +52 -0
- free_claude_code/runtime/__init__.py +1 -0
- free_claude_code/runtime/application.py +1121 -0
- free_claude_code/runtime/asgi.py +63 -0
- free_claude_code/runtime/bootstrap.py +153 -0
- free_claude_code/runtime/code_sessions_sqlite.py +785 -0
- free_claude_code/runtime/codex_app_server.py +775 -0
- free_claude_code/runtime/codex_catalog.py +62 -0
- free_claude_code/runtime/codex_protocol.py +662 -0
- free_claude_code/runtime/configuration.py +53 -0
- free_claude_code/runtime/folder_picker.py +134 -0
- free_claude_code/runtime/native_folder_dialog.py +123 -0
- free_claude_code/runtime/provider_manager.py +716 -0
- free_claude_code/runtime/retired_chat.py +47 -0
- free_claude_code/runtime/web_tools/__init__.py +1 -0
- free_claude_code/runtime/web_tools/client.py +238 -0
- free_claude_code/runtime/web_tools/constants.py +17 -0
- free_claude_code/runtime/web_tools/egress.py +89 -0
- free_claude_code/runtime/web_tools/parsers.py +78 -0
- free_claude_code-6.2.64.data/scripts/fcc-update +4 -0
- free_claude_code-6.2.64.data/scripts/fcc-update.cmd +5 -0
- free_claude_code-6.2.64.dist-info/METADATA +555 -0
- free_claude_code-6.2.64.dist-info/RECORD +429 -0
- free_claude_code-6.2.64.dist-info/WHEEL +4 -0
- free_claude_code-6.2.64.dist-info/entry_points.txt +15 -0
- free_claude_code-6.2.64.dist-info/licenses/LICENSE +239 -0
|
@@ -0,0 +1,306 @@
|
|
|
1
|
+
"""Native Messages event validation and identity-preserving relay."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from collections.abc import Mapping
|
|
5
|
+
from dataclasses import dataclass, field, replace
|
|
6
|
+
from typing import cast
|
|
7
|
+
|
|
8
|
+
from free_claude_code.core.history_replay import (
|
|
9
|
+
ReplayOrigin,
|
|
10
|
+
ReplayRecord,
|
|
11
|
+
encode_replay,
|
|
12
|
+
)
|
|
13
|
+
from free_claude_code.core.json_types import JsonObject, JsonValue
|
|
14
|
+
|
|
15
|
+
from .native import NativeMessagesError
|
|
16
|
+
|
|
17
|
+
_STOP_REASONS = {
|
|
18
|
+
"end_turn",
|
|
19
|
+
"stop_sequence",
|
|
20
|
+
"tool_use",
|
|
21
|
+
"max_tokens",
|
|
22
|
+
"refusal",
|
|
23
|
+
"pause_turn",
|
|
24
|
+
"model_context_window_exceeded",
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
@dataclass(slots=True)
|
|
29
|
+
class _Block:
|
|
30
|
+
body: JsonObject
|
|
31
|
+
parts: list[str] = field(default_factory=list)
|
|
32
|
+
fragments: dict[str, list[str]] = field(default_factory=dict)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
class NativeMessagesStreamState:
|
|
36
|
+
"""Validate one upstream lifecycle without rewriting protocol identities."""
|
|
37
|
+
|
|
38
|
+
def __init__(self) -> None:
|
|
39
|
+
self.started = False
|
|
40
|
+
self.completed = False
|
|
41
|
+
self.stop_reason: str | None = None
|
|
42
|
+
self._blocks: dict[int, _Block] = {}
|
|
43
|
+
self._seen: set[int] = set()
|
|
44
|
+
self._tool_ids: set[str] = set()
|
|
45
|
+
|
|
46
|
+
def accept(
|
|
47
|
+
self, event_type: str, payload: Mapping[str, JsonValue]
|
|
48
|
+
) -> JsonObject | None:
|
|
49
|
+
if self.completed:
|
|
50
|
+
raise NativeMessagesError("Messages event arrived after message_stop.")
|
|
51
|
+
try:
|
|
52
|
+
json.dumps(dict(payload), ensure_ascii=False, allow_nan=False).encode(
|
|
53
|
+
"utf-8"
|
|
54
|
+
)
|
|
55
|
+
except (ValueError, UnicodeError, RecursionError) as exc:
|
|
56
|
+
raise NativeMessagesError(
|
|
57
|
+
"Native Messages event is not representable JSON."
|
|
58
|
+
) from exc
|
|
59
|
+
if payload.get("type") != event_type:
|
|
60
|
+
raise NativeMessagesError("Messages event type disagrees with its payload.")
|
|
61
|
+
if event_type == "ping":
|
|
62
|
+
return None
|
|
63
|
+
if event_type == "message_start":
|
|
64
|
+
if self.started:
|
|
65
|
+
raise NativeMessagesError("Duplicate message_start.")
|
|
66
|
+
message = payload.get("message")
|
|
67
|
+
if (
|
|
68
|
+
not isinstance(message, Mapping)
|
|
69
|
+
or not isinstance(message.get("id"), str)
|
|
70
|
+
or not message["id"]
|
|
71
|
+
or message.get("role") != "assistant"
|
|
72
|
+
or message.get("content", []) != []
|
|
73
|
+
):
|
|
74
|
+
raise NativeMessagesError("Invalid native message_start.")
|
|
75
|
+
self.started = True
|
|
76
|
+
return None
|
|
77
|
+
if not self.started:
|
|
78
|
+
raise NativeMessagesError("Messages event arrived before message_start.")
|
|
79
|
+
if event_type == "message_delta":
|
|
80
|
+
delta = payload.get("delta")
|
|
81
|
+
if not isinstance(delta, Mapping):
|
|
82
|
+
raise NativeMessagesError("Native message_delta requires an object.")
|
|
83
|
+
reason = delta.get("stop_reason")
|
|
84
|
+
if reason is not None:
|
|
85
|
+
if self._blocks:
|
|
86
|
+
raise NativeMessagesError(
|
|
87
|
+
"Stop reason arrived with open content blocks."
|
|
88
|
+
)
|
|
89
|
+
if not isinstance(reason, str) or reason not in _STOP_REASONS:
|
|
90
|
+
raise NativeMessagesError(
|
|
91
|
+
"Unsupported native Messages stop reason."
|
|
92
|
+
)
|
|
93
|
+
if self.stop_reason is not None:
|
|
94
|
+
raise NativeMessagesError("Duplicate native Messages stop reason.")
|
|
95
|
+
self.stop_reason = reason
|
|
96
|
+
return None
|
|
97
|
+
if event_type == "message_stop":
|
|
98
|
+
if self._blocks or self.stop_reason is None:
|
|
99
|
+
raise NativeMessagesError(
|
|
100
|
+
"message_stop arrived before content or stop reason completed."
|
|
101
|
+
)
|
|
102
|
+
self.completed = True
|
|
103
|
+
return None
|
|
104
|
+
if event_type not in {
|
|
105
|
+
"content_block_start",
|
|
106
|
+
"content_block_delta",
|
|
107
|
+
"content_block_stop",
|
|
108
|
+
}:
|
|
109
|
+
raise NativeMessagesError(
|
|
110
|
+
f"Unsupported native Messages event: {event_type!r}."
|
|
111
|
+
)
|
|
112
|
+
if self.stop_reason is not None:
|
|
113
|
+
raise NativeMessagesError("Content arrived after the message stop reason.")
|
|
114
|
+
index = payload.get("index")
|
|
115
|
+
if not isinstance(index, int) or isinstance(index, bool) or index < 0:
|
|
116
|
+
raise NativeMessagesError(
|
|
117
|
+
"Messages block index must be a nonnegative integer."
|
|
118
|
+
)
|
|
119
|
+
if event_type == "content_block_start":
|
|
120
|
+
if index in self._seen:
|
|
121
|
+
raise NativeMessagesError("Duplicate Messages content block index.")
|
|
122
|
+
body = payload.get("content_block")
|
|
123
|
+
if not isinstance(body, Mapping) or not isinstance(body.get("type"), str):
|
|
124
|
+
raise NativeMessagesError("Invalid native content block.")
|
|
125
|
+
kind = body["type"]
|
|
126
|
+
if kind == "text" and not isinstance(body.get("text"), str):
|
|
127
|
+
raise NativeMessagesError("Native text block requires text.")
|
|
128
|
+
if kind == "thinking" and not isinstance(body.get("thinking"), str):
|
|
129
|
+
raise NativeMessagesError(
|
|
130
|
+
"Native thinking block requires thinking text."
|
|
131
|
+
)
|
|
132
|
+
if (
|
|
133
|
+
kind == "thinking"
|
|
134
|
+
and "signature" in body
|
|
135
|
+
and not isinstance(body["signature"], str)
|
|
136
|
+
):
|
|
137
|
+
raise NativeMessagesError("Native thinking signature must be a string.")
|
|
138
|
+
if kind == "redacted_thinking" and (
|
|
139
|
+
not isinstance(body.get("data"), str) or not body["data"]
|
|
140
|
+
):
|
|
141
|
+
raise NativeMessagesError("Redacted native thinking requires its data.")
|
|
142
|
+
if kind == "tool_use":
|
|
143
|
+
tool_id = body.get("id")
|
|
144
|
+
if (
|
|
145
|
+
not isinstance(tool_id, str)
|
|
146
|
+
or not tool_id
|
|
147
|
+
or tool_id in self._tool_ids
|
|
148
|
+
or not isinstance(body.get("name"), str)
|
|
149
|
+
or not body["name"]
|
|
150
|
+
or not isinstance(body.get("input"), Mapping)
|
|
151
|
+
):
|
|
152
|
+
raise NativeMessagesError(
|
|
153
|
+
"Invalid or duplicate native tool identity/input."
|
|
154
|
+
)
|
|
155
|
+
self._tool_ids.add(tool_id)
|
|
156
|
+
self._blocks[index] = _Block(dict(body))
|
|
157
|
+
self._seen.add(index)
|
|
158
|
+
return None
|
|
159
|
+
block = self._blocks.get(index)
|
|
160
|
+
if block is None:
|
|
161
|
+
raise NativeMessagesError(
|
|
162
|
+
"Messages content event refers to a closed or unknown block."
|
|
163
|
+
)
|
|
164
|
+
if event_type == "content_block_delta":
|
|
165
|
+
delta = payload.get("delta")
|
|
166
|
+
if not isinstance(delta, Mapping):
|
|
167
|
+
raise NativeMessagesError("Native content delta must be an object.")
|
|
168
|
+
kind = delta.get("type")
|
|
169
|
+
target = (
|
|
170
|
+
{
|
|
171
|
+
"text_delta": ("text", "text"),
|
|
172
|
+
"thinking_delta": ("thinking", "thinking"),
|
|
173
|
+
"signature_delta": ("thinking", "signature"),
|
|
174
|
+
"input_json_delta": ("tool_use", "partial_json"),
|
|
175
|
+
}.get(kind)
|
|
176
|
+
if isinstance(kind, str)
|
|
177
|
+
else None
|
|
178
|
+
)
|
|
179
|
+
if target is None:
|
|
180
|
+
if kind == "citations_delta" and block.body["type"] == "text":
|
|
181
|
+
citation = delta.get("citation")
|
|
182
|
+
citations = block.body.get("citations")
|
|
183
|
+
if citations is None:
|
|
184
|
+
citations = []
|
|
185
|
+
if not isinstance(citation, Mapping) or not isinstance(
|
|
186
|
+
citations, list
|
|
187
|
+
):
|
|
188
|
+
raise NativeMessagesError("Invalid native citation delta.")
|
|
189
|
+
block.body["citations"] = [*citations, dict(citation)]
|
|
190
|
+
return None
|
|
191
|
+
raise NativeMessagesError("Unsupported native content delta.")
|
|
192
|
+
block_type, key = target
|
|
193
|
+
value = delta.get(key)
|
|
194
|
+
if block.body["type"] != block_type or not isinstance(value, str):
|
|
195
|
+
raise NativeMessagesError(
|
|
196
|
+
"Native content delta does not match its block."
|
|
197
|
+
)
|
|
198
|
+
if key == "partial_json":
|
|
199
|
+
if block.body.get("input"):
|
|
200
|
+
raise NativeMessagesError(
|
|
201
|
+
"Tool input mixes eager content and JSON deltas."
|
|
202
|
+
)
|
|
203
|
+
block.parts.append(value)
|
|
204
|
+
else:
|
|
205
|
+
existing = block.body.get(key, "")
|
|
206
|
+
if not isinstance(existing, str):
|
|
207
|
+
raise NativeMessagesError("Native content field must be a string.")
|
|
208
|
+
block.fragments.setdefault(key, []).append(value)
|
|
209
|
+
return None
|
|
210
|
+
for key, parts in block.fragments.items():
|
|
211
|
+
initial = block.body.get(key, "")
|
|
212
|
+
if not isinstance(initial, str):
|
|
213
|
+
raise AssertionError("Validated native content fields must be strings.")
|
|
214
|
+
block.body[key] = initial + "".join(parts)
|
|
215
|
+
if block.body["type"] == "thinking" and not block.body.get("signature"):
|
|
216
|
+
raise NativeMessagesError(
|
|
217
|
+
"Completed native thinking requires its signature."
|
|
218
|
+
)
|
|
219
|
+
if block.body["type"] == "tool_use" and block.parts:
|
|
220
|
+
try:
|
|
221
|
+
value = cast(JsonValue, json.loads("".join(block.parts)))
|
|
222
|
+
if not isinstance(value, Mapping):
|
|
223
|
+
raise NativeMessagesError(
|
|
224
|
+
"Native tool arguments must decode to an object."
|
|
225
|
+
)
|
|
226
|
+
json.dumps(value, ensure_ascii=False, allow_nan=False).encode("utf-8")
|
|
227
|
+
except (ValueError, UnicodeError, RecursionError) as exc:
|
|
228
|
+
raise NativeMessagesError(
|
|
229
|
+
"Native tool arguments are incomplete or invalid JSON."
|
|
230
|
+
) from exc
|
|
231
|
+
block.body["input"] = dict(value)
|
|
232
|
+
self._blocks.pop(index)
|
|
233
|
+
return block.body
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
class NativeMessagesRelay:
|
|
237
|
+
"""Preserve upstream IDs, indexes, fields and event order for Messages clients."""
|
|
238
|
+
|
|
239
|
+
def __init__(
|
|
240
|
+
self, *, public_model: str, replay_origin: ReplayOrigin | None = None
|
|
241
|
+
) -> None:
|
|
242
|
+
self._public_model = public_model
|
|
243
|
+
self._replay_origin = replay_origin
|
|
244
|
+
self._state = NativeMessagesStreamState()
|
|
245
|
+
|
|
246
|
+
@property
|
|
247
|
+
def completed(self) -> bool:
|
|
248
|
+
return self._state.completed
|
|
249
|
+
|
|
250
|
+
@property
|
|
251
|
+
def stop_reason(self) -> str | None:
|
|
252
|
+
return self._state.stop_reason
|
|
253
|
+
|
|
254
|
+
def feed(self, event_type: str, payload: Mapping[str, JsonValue]) -> str:
|
|
255
|
+
completed = self._state.accept(event_type, payload)
|
|
256
|
+
body = dict(payload)
|
|
257
|
+
if event_type == "message_start":
|
|
258
|
+
message = body.get("message")
|
|
259
|
+
if not isinstance(message, dict):
|
|
260
|
+
raise AssertionError("Validated message_start must contain a message.")
|
|
261
|
+
body["message"] = {**message, "model": self._public_model}
|
|
262
|
+
if (
|
|
263
|
+
self._replay_origin is not None
|
|
264
|
+
and isinstance(message.get("model"), str)
|
|
265
|
+
and message["model"]
|
|
266
|
+
):
|
|
267
|
+
self._replay_origin = replace(
|
|
268
|
+
self._replay_origin, model=message["model"]
|
|
269
|
+
)
|
|
270
|
+
prefix = ""
|
|
271
|
+
if self._replay_origin is not None:
|
|
272
|
+
block = body.get("content_block")
|
|
273
|
+
if event_type == "content_block_start" and isinstance(block, dict):
|
|
274
|
+
if block.get("type") == "thinking":
|
|
275
|
+
body["content_block"] = {**block, "signature": ""}
|
|
276
|
+
elif block.get("type") == "redacted_thinking":
|
|
277
|
+
body["content_block"] = {
|
|
278
|
+
**block,
|
|
279
|
+
"data": encode_replay(ReplayRecord(self._replay_origin, block)),
|
|
280
|
+
}
|
|
281
|
+
delta = body.get("delta")
|
|
282
|
+
if (
|
|
283
|
+
event_type == "content_block_delta"
|
|
284
|
+
and isinstance(delta, dict)
|
|
285
|
+
and delta.get("type") == "signature_delta"
|
|
286
|
+
):
|
|
287
|
+
return ""
|
|
288
|
+
if (
|
|
289
|
+
completed is not None
|
|
290
|
+
and completed.get("type") == "thinking"
|
|
291
|
+
and completed.get("signature")
|
|
292
|
+
):
|
|
293
|
+
signature = encode_replay(ReplayRecord(self._replay_origin, completed))
|
|
294
|
+
prefix = _event(
|
|
295
|
+
"content_block_delta",
|
|
296
|
+
{
|
|
297
|
+
"type": "content_block_delta",
|
|
298
|
+
"index": body["index"],
|
|
299
|
+
"delta": {"type": "signature_delta", "signature": signature},
|
|
300
|
+
},
|
|
301
|
+
)
|
|
302
|
+
return prefix + _event(event_type, body)
|
|
303
|
+
|
|
304
|
+
|
|
305
|
+
def _event(kind: str, payload: JsonObject) -> str:
|
|
306
|
+
return f"event: {kind}\ndata: {json.dumps(payload, ensure_ascii=False)}\n\n"
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"""Shared Anthropic request serialization helpers."""
|
|
2
|
+
|
|
3
|
+
import json
|
|
4
|
+
from typing import Any
|
|
5
|
+
|
|
6
|
+
from .models import MessagesRequest
|
|
7
|
+
|
|
8
|
+
_MESSAGES_REQUEST_FIELDS = (
|
|
9
|
+
"model",
|
|
10
|
+
"messages",
|
|
11
|
+
"system",
|
|
12
|
+
"max_tokens",
|
|
13
|
+
"stop_sequences",
|
|
14
|
+
"stream",
|
|
15
|
+
"temperature",
|
|
16
|
+
"top_p",
|
|
17
|
+
"top_k",
|
|
18
|
+
"metadata",
|
|
19
|
+
"tools",
|
|
20
|
+
"tool_choice",
|
|
21
|
+
"thinking",
|
|
22
|
+
"context_management",
|
|
23
|
+
"output_config",
|
|
24
|
+
"mcp_servers",
|
|
25
|
+
"extra_body",
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def dump_messages_request(request: MessagesRequest) -> dict[str, Any]:
|
|
30
|
+
"""Return JSON-ready public Messages fields without FCC routing state."""
|
|
31
|
+
raw = request.model_dump(exclude_none=True)
|
|
32
|
+
return {
|
|
33
|
+
field: raw[field]
|
|
34
|
+
for field in _MESSAGES_REQUEST_FIELDS
|
|
35
|
+
if field in raw and raw[field] is not None
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def serialize_tool_result_content(content: Any) -> str:
|
|
40
|
+
"""Serialize Anthropic ``tool_result.content`` into provider-safe text."""
|
|
41
|
+
if content is None:
|
|
42
|
+
return ""
|
|
43
|
+
if isinstance(content, str):
|
|
44
|
+
return content
|
|
45
|
+
if isinstance(content, dict):
|
|
46
|
+
return json.dumps(content, ensure_ascii=False)
|
|
47
|
+
if isinstance(content, list):
|
|
48
|
+
parts: list[str] = []
|
|
49
|
+
for item in content:
|
|
50
|
+
if isinstance(item, dict) and item.get("type") == "text":
|
|
51
|
+
parts.append(str(item.get("text", "")))
|
|
52
|
+
elif isinstance(item, dict):
|
|
53
|
+
parts.append(json.dumps(item, ensure_ascii=False))
|
|
54
|
+
else:
|
|
55
|
+
parts.append(str(item))
|
|
56
|
+
return "\n".join(parts)
|
|
57
|
+
return str(content)
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
"""Trace-safe snapshots of Anthropic protocol requests."""
|
|
2
|
+
|
|
3
|
+
from typing import Any
|
|
4
|
+
|
|
5
|
+
from free_claude_code.core.trace import sanitize_trace_value
|
|
6
|
+
|
|
7
|
+
from .models import MessagesRequest, TokenCountRequest
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def anthropic_request_snapshot(
|
|
11
|
+
request: MessagesRequest | TokenCountRequest,
|
|
12
|
+
) -> dict[str, Any]:
|
|
13
|
+
"""Return the traceable public fields of an Anthropic request."""
|
|
14
|
+
data = request.model_dump(mode="python")
|
|
15
|
+
snapshot = {
|
|
16
|
+
key: data[key]
|
|
17
|
+
for key in (
|
|
18
|
+
"model",
|
|
19
|
+
"messages",
|
|
20
|
+
"system",
|
|
21
|
+
"tools",
|
|
22
|
+
"tool_choice",
|
|
23
|
+
"max_tokens",
|
|
24
|
+
"thinking",
|
|
25
|
+
"temperature",
|
|
26
|
+
"top_p",
|
|
27
|
+
"top_k",
|
|
28
|
+
"stop_sequences",
|
|
29
|
+
"metadata",
|
|
30
|
+
"stream",
|
|
31
|
+
)
|
|
32
|
+
if key in data and data[key] is not None
|
|
33
|
+
}
|
|
34
|
+
sanitized = sanitize_trace_value(snapshot)
|
|
35
|
+
return sanitized if isinstance(sanitized, dict) else {}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
"""Anthropic representation of completed local web-tool operations."""
|
|
2
|
+
|
|
3
|
+
from collections.abc import Iterator, Mapping, Sequence
|
|
4
|
+
from dataclasses import dataclass
|
|
5
|
+
|
|
6
|
+
from free_claude_code.core.json_types import JsonObject
|
|
7
|
+
from free_claude_code.core.web_tools import WebFetchResult, WebSearchResult
|
|
8
|
+
|
|
9
|
+
from .server_tool_types import (
|
|
10
|
+
SERVER_TOOL_USE,
|
|
11
|
+
WEB_FETCH_TOOL_ERROR,
|
|
12
|
+
WEB_FETCH_TOOL_RESULT,
|
|
13
|
+
WEB_SEARCH_TOOL_RESULT,
|
|
14
|
+
WEB_SEARCH_TOOL_RESULT_ERROR,
|
|
15
|
+
)
|
|
16
|
+
from .streaming import format_sse_event
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass(frozen=True, slots=True)
|
|
20
|
+
class ServerToolResponseContext:
|
|
21
|
+
message_id: str
|
|
22
|
+
tool_id: str
|
|
23
|
+
model: str
|
|
24
|
+
tool_name: str
|
|
25
|
+
tool_input: Mapping[str, str]
|
|
26
|
+
input_tokens: int
|
|
27
|
+
provider_usage: Mapping[str, object] | None = None
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def server_tool_start_frames(context: ServerToolResponseContext) -> Iterator[str]:
|
|
31
|
+
yield from server_tool_message_start_frames(context)
|
|
32
|
+
yield from server_tool_use_frames(context, index=0)
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def server_tool_message_start_frames(
|
|
36
|
+
context: ServerToolResponseContext,
|
|
37
|
+
) -> Iterator[str]:
|
|
38
|
+
yield format_sse_event(
|
|
39
|
+
"message_start",
|
|
40
|
+
{
|
|
41
|
+
"type": "message_start",
|
|
42
|
+
"message": {
|
|
43
|
+
"id": context.message_id,
|
|
44
|
+
"type": "message",
|
|
45
|
+
"role": "assistant",
|
|
46
|
+
"content": [],
|
|
47
|
+
"model": context.model,
|
|
48
|
+
"stop_reason": None,
|
|
49
|
+
"stop_sequence": None,
|
|
50
|
+
"usage": _message_start_usage(
|
|
51
|
+
context.input_tokens, context.provider_usage
|
|
52
|
+
),
|
|
53
|
+
},
|
|
54
|
+
},
|
|
55
|
+
)
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
def server_tool_use_frames(
|
|
59
|
+
context: ServerToolResponseContext, *, index: int
|
|
60
|
+
) -> Iterator[str]:
|
|
61
|
+
yield format_sse_event(
|
|
62
|
+
"content_block_start",
|
|
63
|
+
{
|
|
64
|
+
"type": "content_block_start",
|
|
65
|
+
"index": index,
|
|
66
|
+
"content_block": {
|
|
67
|
+
"type": SERVER_TOOL_USE,
|
|
68
|
+
"id": context.tool_id,
|
|
69
|
+
"name": context.tool_name,
|
|
70
|
+
"input": context.tool_input,
|
|
71
|
+
},
|
|
72
|
+
},
|
|
73
|
+
)
|
|
74
|
+
yield format_sse_event(
|
|
75
|
+
"content_block_stop", {"type": "content_block_stop", "index": index}
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def web_search_result_block(
|
|
80
|
+
context: ServerToolResponseContext, results: Sequence[WebSearchResult]
|
|
81
|
+
) -> JsonObject:
|
|
82
|
+
return {
|
|
83
|
+
"type": WEB_SEARCH_TOOL_RESULT,
|
|
84
|
+
"tool_use_id": context.tool_id,
|
|
85
|
+
"content": [
|
|
86
|
+
{"type": "web_search_result", "title": result.title, "url": result.url}
|
|
87
|
+
for result in results
|
|
88
|
+
],
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def web_fetch_result_block(
|
|
93
|
+
context: ServerToolResponseContext,
|
|
94
|
+
result: WebFetchResult,
|
|
95
|
+
*,
|
|
96
|
+
retrieved_at: str,
|
|
97
|
+
) -> JsonObject:
|
|
98
|
+
return {
|
|
99
|
+
"type": WEB_FETCH_TOOL_RESULT,
|
|
100
|
+
"tool_use_id": context.tool_id,
|
|
101
|
+
"content": {
|
|
102
|
+
"type": "web_fetch_result",
|
|
103
|
+
"url": result.url,
|
|
104
|
+
"content": {
|
|
105
|
+
"type": "document",
|
|
106
|
+
"source": {
|
|
107
|
+
"type": "text",
|
|
108
|
+
"media_type": result.media_type,
|
|
109
|
+
"data": result.data,
|
|
110
|
+
},
|
|
111
|
+
"title": result.title,
|
|
112
|
+
"citations": {"enabled": True},
|
|
113
|
+
},
|
|
114
|
+
"retrieved_at": retrieved_at,
|
|
115
|
+
},
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def web_tool_error_block(
|
|
120
|
+
context: ServerToolResponseContext, *, error_code: str = "unavailable"
|
|
121
|
+
) -> JsonObject:
|
|
122
|
+
search = context.tool_name == "web_search"
|
|
123
|
+
return {
|
|
124
|
+
"type": WEB_SEARCH_TOOL_RESULT if search else WEB_FETCH_TOOL_RESULT,
|
|
125
|
+
"tool_use_id": context.tool_id,
|
|
126
|
+
"content": {
|
|
127
|
+
"type": WEB_SEARCH_TOOL_RESULT_ERROR if search else WEB_FETCH_TOOL_ERROR,
|
|
128
|
+
"error_code": error_code,
|
|
129
|
+
},
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
def server_tool_completion_frames(
|
|
134
|
+
context: ServerToolResponseContext,
|
|
135
|
+
result_block: JsonObject,
|
|
136
|
+
*,
|
|
137
|
+
summary: str,
|
|
138
|
+
) -> Iterator[str]:
|
|
139
|
+
yield from server_tool_result_frames(result_block, index=1)
|
|
140
|
+
yield from server_tool_finish_frames(context, summary=summary, index=2)
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def server_tool_result_frames(result_block: JsonObject, *, index: int) -> Iterator[str]:
|
|
144
|
+
yield format_sse_event(
|
|
145
|
+
"content_block_start",
|
|
146
|
+
{"type": "content_block_start", "index": index, "content_block": result_block},
|
|
147
|
+
)
|
|
148
|
+
yield format_sse_event(
|
|
149
|
+
"content_block_stop", {"type": "content_block_stop", "index": index}
|
|
150
|
+
)
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
def server_tool_finish_frames(
|
|
154
|
+
context: ServerToolResponseContext,
|
|
155
|
+
*,
|
|
156
|
+
summary: str,
|
|
157
|
+
index: int,
|
|
158
|
+
request_count: int = 1,
|
|
159
|
+
) -> Iterator[str]:
|
|
160
|
+
yield format_sse_event(
|
|
161
|
+
"content_block_start",
|
|
162
|
+
{
|
|
163
|
+
"type": "content_block_start",
|
|
164
|
+
"index": index,
|
|
165
|
+
"content_block": {"type": "text", "text": ""},
|
|
166
|
+
},
|
|
167
|
+
)
|
|
168
|
+
yield format_sse_event(
|
|
169
|
+
"content_block_delta",
|
|
170
|
+
{
|
|
171
|
+
"type": "content_block_delta",
|
|
172
|
+
"index": index,
|
|
173
|
+
"delta": {"type": "text_delta", "text": summary},
|
|
174
|
+
},
|
|
175
|
+
)
|
|
176
|
+
yield format_sse_event(
|
|
177
|
+
"content_block_stop", {"type": "content_block_stop", "index": index}
|
|
178
|
+
)
|
|
179
|
+
yield format_sse_event(
|
|
180
|
+
"message_delta",
|
|
181
|
+
{
|
|
182
|
+
"type": "message_delta",
|
|
183
|
+
"delta": {"stop_reason": "end_turn", "stop_sequence": None},
|
|
184
|
+
"usage": _completion_usage(
|
|
185
|
+
context.input_tokens,
|
|
186
|
+
summary,
|
|
187
|
+
context.provider_usage,
|
|
188
|
+
usage_key=f"{context.tool_name}_requests",
|
|
189
|
+
request_count=request_count,
|
|
190
|
+
),
|
|
191
|
+
},
|
|
192
|
+
)
|
|
193
|
+
yield format_sse_event("message_stop", {"type": "message_stop"})
|
|
194
|
+
|
|
195
|
+
|
|
196
|
+
def _message_start_usage(
|
|
197
|
+
input_tokens: int,
|
|
198
|
+
provider_usage: Mapping[str, object] | None,
|
|
199
|
+
) -> dict[str, object]:
|
|
200
|
+
if provider_usage is None:
|
|
201
|
+
return {"input_tokens": input_tokens, "output_tokens": 1}
|
|
202
|
+
usage = _integer_usage(provider_usage)
|
|
203
|
+
usage.setdefault("input_tokens", input_tokens)
|
|
204
|
+
usage["output_tokens"] = 1
|
|
205
|
+
return usage
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
def _completion_usage(
|
|
209
|
+
input_tokens: int,
|
|
210
|
+
summary: str,
|
|
211
|
+
provider_usage: Mapping[str, object] | None,
|
|
212
|
+
*,
|
|
213
|
+
usage_key: str,
|
|
214
|
+
request_count: int,
|
|
215
|
+
) -> dict[str, object]:
|
|
216
|
+
if provider_usage is None:
|
|
217
|
+
usage: dict[str, object] = {
|
|
218
|
+
"input_tokens": input_tokens,
|
|
219
|
+
"output_tokens": max(1, len(summary) // 4),
|
|
220
|
+
}
|
|
221
|
+
else:
|
|
222
|
+
usage = _integer_usage(provider_usage)
|
|
223
|
+
usage.setdefault("input_tokens", input_tokens)
|
|
224
|
+
usage.setdefault("output_tokens", max(1, len(summary) // 4))
|
|
225
|
+
usage["server_tool_use"] = {usage_key: request_count}
|
|
226
|
+
return usage
|
|
227
|
+
|
|
228
|
+
|
|
229
|
+
def _integer_usage(usage: Mapping[str, object]) -> dict[str, object]:
|
|
230
|
+
return {
|
|
231
|
+
key: value
|
|
232
|
+
for key, value in usage.items()
|
|
233
|
+
if isinstance(value, int) and not isinstance(value, bool)
|
|
234
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"""Anthropic server-tool block types shared by decoders and formatters."""
|
|
2
|
+
|
|
3
|
+
from typing import Final
|
|
4
|
+
|
|
5
|
+
SERVER_TOOL_USE: Final = "server_tool_use"
|
|
6
|
+
WEB_SEARCH_TOOL_RESULT: Final = "web_search_tool_result"
|
|
7
|
+
WEB_FETCH_TOOL_RESULT: Final = "web_fetch_tool_result"
|
|
8
|
+
WEB_SEARCH_TOOL_RESULT_ERROR: Final = "web_search_tool_result_error"
|
|
9
|
+
WEB_FETCH_TOOL_ERROR: Final = "web_fetch_tool_error"
|