mcp-mesh 0.6.2__tar.gz → 0.6.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/PKG-INFO +1 -1
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/__init__.py +1 -1
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/llm_config.py +10 -1
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/mesh_llm_agent.py +51 -33
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/mesh_llm_agent_injector.py +19 -0
- mcp_mesh-0.6.3/_mcp_mesh/engine/provider_handlers/claude_handler.py +418 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/provider_handlers/openai_handler.py +65 -9
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/response_parser.py +54 -15
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/mesh/decorators.py +39 -2
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/pyproject.toml +4 -4
- mcp_mesh-0.6.2/_mcp_mesh/engine/provider_handlers/claude_handler.py +0 -138
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/.gitignore +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/LICENSE +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/README.md +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/__init__.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/async_mcp_client.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/base_injector.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/decorator_registry.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/dependency_injector.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/full_mcp_proxy.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/http_wrapper.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/llm_errors.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/mcp_client_proxy.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/provider_handlers/__init__.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/provider_handlers/base_provider_handler.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/provider_handlers/generic_handler.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/provider_handlers/provider_handler_registry.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/self_dependency_proxy.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/session_aware_client.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/session_manager.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/signature_analyzer.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/tool_executor.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/tool_schema_builder.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/engine/unified_mcp_proxy.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/.openapi-generator/FILES +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/.openapi-generator/VERSION +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/.openapi-generator-ignore +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/__init__.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/api/__init__.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/api/agents_api.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/api/health_api.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/api/tracing_api.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/api_client.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/api_response.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/configuration.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/exceptions.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/__init__.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/agent_info.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata_dependencies_inner.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/agent_metadata_dependencies_inner_one_of.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/agent_registration.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/agent_registration_metadata.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/agents_list_response.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/capability_info.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/decorator_agent_metadata.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/decorator_agent_request.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/decorator_info.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/dependency_info.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/dependency_resolution_info.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/error_response.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/health_response.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_request.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_request_metadata.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/heartbeat_response.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/llm_provider.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_filter.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_filter_filter_inner.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_filter_filter_inner_one_of.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/llm_tool_info.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_agent_register_metadata.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_agent_registration.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_registration_response.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_registration_response_dependencies_resolved_value_inner.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_tool_dependency_registration.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_tool_register_metadata.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/mesh_tool_registration.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/registration_response.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/resolved_llm_provider.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/rich_dependency.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/root_response.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/standardized_dependency.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/models/trace_event.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/py.typed +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/generated/mcp_mesh_registry_client/rest.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/__init__.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/api_heartbeat/__init__.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/api_heartbeat/api_dependency_resolution.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/api_heartbeat/api_fast_heartbeat_check.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/api_heartbeat/api_health_check.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/api_heartbeat/api_heartbeat_orchestrator.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/api_heartbeat/api_heartbeat_pipeline.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/api_heartbeat/api_heartbeat_send.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/api_heartbeat/api_lifespan_integration.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/api_heartbeat/api_registry_connection.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/api_startup/__init__.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/api_startup/api_pipeline.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/api_startup/api_server_setup.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/api_startup/fastapi_discovery.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/api_startup/middleware_integration.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/api_startup/route_collection.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/api_startup/route_integration.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/mcp_heartbeat/__init__.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/mcp_heartbeat/dependency_resolution.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/mcp_heartbeat/fast_heartbeat_check.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/mcp_heartbeat/heartbeat_orchestrator.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/mcp_heartbeat/heartbeat_pipeline.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/mcp_heartbeat/heartbeat_send.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/mcp_heartbeat/lifespan_integration.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/mcp_heartbeat/llm_tools_resolution.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/mcp_heartbeat/registry_connection.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/mcp_startup/__init__.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/mcp_startup/configuration.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/mcp_startup/decorator_collection.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/mcp_startup/fastapiserver_setup.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/mcp_startup/fastmcpserver_discovery.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/mcp_startup/heartbeat_loop.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/mcp_startup/heartbeat_preparation.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/mcp_startup/server_discovery.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/mcp_startup/startup_orchestrator.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/mcp_startup/startup_pipeline.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/shared/__init__.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/shared/base_step.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/shared/mesh_pipeline.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/shared/pipeline_types.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/pipeline/shared/registry_connection.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/shared/__init__.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/shared/config_resolver.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/shared/content_extractor.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/shared/defaults.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/shared/fast_heartbeat_status.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/shared/fastapi_middleware_manager.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/shared/health_check_cache.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/shared/host_resolver.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/shared/logging_config.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/shared/registry_client_wrapper.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/shared/server_discovery.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/shared/simple_shutdown.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/shared/sse_parser.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/shared/support_types.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/tracing/agent_context_helper.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/tracing/context.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/tracing/execution_tracer.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/tracing/fastapi_tracing_middleware.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/tracing/redis_metadata_publisher.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/tracing/trace_context_helper.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/tracing/utils.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/_mcp_mesh/utils/fastmcp_schema_extractor.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/mesh/__init__.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/mesh/helpers.py +0 -0
- {mcp_mesh-0.6.2 → mcp_mesh-0.6.3}/mesh/types.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mcp-mesh
|
|
3
|
-
Version: 0.6.
|
|
3
|
+
Version: 0.6.3
|
|
4
4
|
Summary: Kubernetes-native platform for distributed MCP applications
|
|
5
5
|
Project-URL: Homepage, https://github.com/dhyansraj/mcp-mesh
|
|
6
6
|
Project-URL: Documentation, https://github.com/dhyansraj/mcp-mesh/tree/main/docs
|
|
@@ -17,7 +17,7 @@ class LLMConfig:
|
|
|
17
17
|
Supports both direct LiteLLM providers (string) and mesh delegation (dict).
|
|
18
18
|
"""
|
|
19
19
|
|
|
20
|
-
provider: Union[str,
|
|
20
|
+
provider: Union[str, dict[str, Any]] = "claude"
|
|
21
21
|
"""LLM provider - string for direct LiteLLM (e.g., 'claude', 'openai') or dict for mesh delegation
|
|
22
22
|
Mesh delegation format: {"capability": "llm", "tags": ["claude"], "version": ">=1.0.0"}"""
|
|
23
23
|
|
|
@@ -33,6 +33,9 @@ class LLMConfig:
|
|
|
33
33
|
system_prompt: Optional[str] = None
|
|
34
34
|
"""Optional system prompt to prepend to all interactions"""
|
|
35
35
|
|
|
36
|
+
output_mode: Optional[str] = None
|
|
37
|
+
"""Output mode override: 'strict', 'hint', or 'text'. If None, auto-detected by handler."""
|
|
38
|
+
|
|
36
39
|
def __post_init__(self):
|
|
37
40
|
"""Validate configuration after initialization."""
|
|
38
41
|
if self.max_iterations < 1:
|
|
@@ -43,3 +46,9 @@ class LLMConfig:
|
|
|
43
46
|
# Only validate model for string providers (not needed for mesh delegation)
|
|
44
47
|
if isinstance(self.provider, str) and not self.model:
|
|
45
48
|
raise ValueError("model cannot be empty when using string provider")
|
|
49
|
+
|
|
50
|
+
# Validate output_mode if provided
|
|
51
|
+
if self.output_mode and self.output_mode not in ("strict", "hint", "text"):
|
|
52
|
+
raise ValueError(
|
|
53
|
+
f"output_mode must be 'strict', 'hint', or 'text', got '{self.output_mode}'"
|
|
54
|
+
)
|
|
@@ -58,7 +58,7 @@ class MeshLlmAgent:
|
|
|
58
58
|
self,
|
|
59
59
|
config: LLMConfig,
|
|
60
60
|
filtered_tools: list[dict[str, Any]],
|
|
61
|
-
output_type: type[BaseModel],
|
|
61
|
+
output_type: type[BaseModel] | type[str],
|
|
62
62
|
tool_proxies: Optional[dict[str, Any]] = None,
|
|
63
63
|
template_path: Optional[str] = None,
|
|
64
64
|
context_value: Optional[Any] = None,
|
|
@@ -71,7 +71,7 @@ class MeshLlmAgent:
|
|
|
71
71
|
Args:
|
|
72
72
|
config: LLM configuration (provider, model, api_key, etc.)
|
|
73
73
|
filtered_tools: List of tool metadata from registry (for schema building)
|
|
74
|
-
output_type: Pydantic BaseModel for response validation
|
|
74
|
+
output_type: Pydantic BaseModel for response validation, or str for plain text
|
|
75
75
|
tool_proxies: Optional map of function_name -> proxy for tool execution
|
|
76
76
|
template_path: Optional path to Jinja2 template file for system prompt
|
|
77
77
|
context_value: Optional context for template rendering (MeshContextModel, dict, or None)
|
|
@@ -87,6 +87,7 @@ class MeshLlmAgent:
|
|
|
87
87
|
self.max_iterations = config.max_iterations
|
|
88
88
|
self.output_type = output_type
|
|
89
89
|
self.system_prompt = config.system_prompt # Public attribute for tests
|
|
90
|
+
self.output_mode = config.output_mode # Output mode override (strict/hint/text)
|
|
90
91
|
self._iteration_count = 0
|
|
91
92
|
|
|
92
93
|
# Detect if using mesh delegation (provider is dict)
|
|
@@ -126,12 +127,19 @@ IMPORTANT TOOL CALLING RULES:
|
|
|
126
127
|
- Once you have gathered all necessary information, provide your final response
|
|
127
128
|
"""
|
|
128
129
|
|
|
129
|
-
schema
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
130
|
+
# Only generate JSON schema for Pydantic models, not for str return type
|
|
131
|
+
if self.output_type is not str and hasattr(
|
|
132
|
+
self.output_type, "model_json_schema"
|
|
133
|
+
):
|
|
134
|
+
schema = self.output_type.model_json_schema()
|
|
135
|
+
schema_str = json.dumps(schema, indent=2)
|
|
136
|
+
self._cached_json_instructions = (
|
|
137
|
+
f"\n\nIMPORTANT: You must return your final response as valid JSON matching this schema:\n"
|
|
138
|
+
f"{schema_str}\n\nReturn ONLY the JSON object, no additional text."
|
|
139
|
+
)
|
|
140
|
+
else:
|
|
141
|
+
# str return type - no JSON schema needed
|
|
142
|
+
self._cached_json_instructions = ""
|
|
135
143
|
|
|
136
144
|
logger.debug(
|
|
137
145
|
f"🤖 MeshLlmAgent initialized: provider={config.provider}, model={config.model}, "
|
|
@@ -483,22 +491,36 @@ IMPORTANT TOOL CALLING RULES:
|
|
|
483
491
|
try:
|
|
484
492
|
# Call LLM (either direct LiteLLM or mesh-delegated)
|
|
485
493
|
try:
|
|
486
|
-
if
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
494
|
+
# Build kwargs with output_mode override if set
|
|
495
|
+
call_kwargs = (
|
|
496
|
+
{**kwargs, "output_mode": self.output_mode}
|
|
497
|
+
if self.output_mode
|
|
498
|
+
else kwargs
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
# Use provider handler to prepare vendor-specific request
|
|
502
|
+
request_params = self._provider_handler.prepare_request(
|
|
503
|
+
messages=messages,
|
|
504
|
+
tools=self._tool_schemas if self._tool_schemas else None,
|
|
505
|
+
output_type=self.output_type,
|
|
506
|
+
**call_kwargs,
|
|
507
|
+
)
|
|
495
508
|
|
|
496
|
-
|
|
497
|
-
#
|
|
509
|
+
if self._is_mesh_delegated:
|
|
510
|
+
# Mesh delegation: extract model_params to send to provider
|
|
511
|
+
# Exclude messages/tools (separate params), model/api_key (provider has them),
|
|
512
|
+
# and output_mode (only used locally by prepare_request)
|
|
498
513
|
model_params = {
|
|
499
514
|
k: v
|
|
500
515
|
for k, v in request_params.items()
|
|
501
|
-
if k
|
|
516
|
+
if k
|
|
517
|
+
not in [
|
|
518
|
+
"messages",
|
|
519
|
+
"tools",
|
|
520
|
+
"model",
|
|
521
|
+
"api_key",
|
|
522
|
+
"output_mode",
|
|
523
|
+
]
|
|
502
524
|
}
|
|
503
525
|
|
|
504
526
|
logger.debug(
|
|
@@ -509,19 +531,10 @@ IMPORTANT TOOL CALLING RULES:
|
|
|
509
531
|
response = await self._call_mesh_provider(
|
|
510
532
|
messages=messages,
|
|
511
533
|
tools=self._tool_schemas if self._tool_schemas else None,
|
|
512
|
-
**model_params,
|
|
534
|
+
**model_params,
|
|
513
535
|
)
|
|
514
536
|
else:
|
|
515
|
-
# Direct LiteLLM call
|
|
516
|
-
# Phase 2: Use provider handler to prepare vendor-specific request
|
|
517
|
-
request_params = self._provider_handler.prepare_request(
|
|
518
|
-
messages=messages,
|
|
519
|
-
tools=self._tool_schemas if self._tool_schemas else None,
|
|
520
|
-
output_type=self.output_type,
|
|
521
|
-
**kwargs,
|
|
522
|
-
)
|
|
523
|
-
|
|
524
|
-
# Add model and API key (common to all vendors)
|
|
537
|
+
# Direct LiteLLM call: add model and API key
|
|
525
538
|
request_params["model"] = self.model
|
|
526
539
|
request_params["api_key"] = self.api_key
|
|
527
540
|
|
|
@@ -612,15 +625,20 @@ IMPORTANT TOOL CALLING RULES:
|
|
|
612
625
|
"""
|
|
613
626
|
Parse LLM response into output type.
|
|
614
627
|
|
|
615
|
-
|
|
628
|
+
For str return type, returns content directly without parsing.
|
|
629
|
+
For Pydantic models, delegates to ResponseParser.
|
|
616
630
|
|
|
617
631
|
Args:
|
|
618
632
|
content: Response content from LLM
|
|
619
633
|
|
|
620
634
|
Returns:
|
|
621
|
-
|
|
635
|
+
Raw string (if output_type is str) or parsed Pydantic model instance
|
|
622
636
|
|
|
623
637
|
Raises:
|
|
624
638
|
ResponseParseError: If response doesn't match output_type schema or invalid JSON
|
|
625
639
|
"""
|
|
640
|
+
# For str return type, return content directly without parsing
|
|
641
|
+
if self.output_type is str:
|
|
642
|
+
return content
|
|
643
|
+
|
|
626
644
|
return ResponseParser.parse(content, self.output_type)
|
|
@@ -307,6 +307,22 @@ class MeshLlmAgentInjector(BaseInjector):
|
|
|
307
307
|
llm_agent = self._create_llm_agent(function_id)
|
|
308
308
|
wrapper._mesh_update_llm_agent(llm_agent)
|
|
309
309
|
logger.info(f"🔄 Updated wrapper with MeshLlmAgent for '{function_id}'")
|
|
310
|
+
|
|
311
|
+
# Set factory for per-call context agent creation (template support)
|
|
312
|
+
# This allows the decorator's wrapper to create new agents with context per-call
|
|
313
|
+
config_dict = llm_metadata.config
|
|
314
|
+
if config_dict.get("is_template", False):
|
|
315
|
+
# Capture function_id by value using default argument to avoid closure issues
|
|
316
|
+
def create_context_agent(
|
|
317
|
+
context_value: Any, _func_id: str = function_id
|
|
318
|
+
) -> MeshLlmAgent:
|
|
319
|
+
"""Factory to create MeshLlmAgent with context for template rendering."""
|
|
320
|
+
return self._create_llm_agent(_func_id, context_value=context_value)
|
|
321
|
+
|
|
322
|
+
wrapper._mesh_create_context_agent = create_context_agent
|
|
323
|
+
logger.info(
|
|
324
|
+
f"🎯 Set context agent factory for template-based function '{function_id}'"
|
|
325
|
+
)
|
|
310
326
|
elif wrapper:
|
|
311
327
|
logger.warning(
|
|
312
328
|
f"⚠️ Wrapper for '{function_id}' found but has no _mesh_update_llm_agent method"
|
|
@@ -512,6 +528,9 @@ class MeshLlmAgentInjector(BaseInjector):
|
|
|
512
528
|
api_key=config_dict.get("api_key", ""), # Will use ENV if empty
|
|
513
529
|
max_iterations=config_dict.get("max_iterations", 10),
|
|
514
530
|
system_prompt=config_dict.get("system_prompt"),
|
|
531
|
+
output_mode=config_dict.get(
|
|
532
|
+
"output_mode"
|
|
533
|
+
), # Pass through output_mode from decorator
|
|
515
534
|
)
|
|
516
535
|
|
|
517
536
|
# Phase 4: Template support - extract template metadata
|
|
@@ -0,0 +1,418 @@
|
|
|
1
|
+
"""
|
|
2
|
+
Claude/Anthropic provider handler.
|
|
3
|
+
|
|
4
|
+
Optimized for Claude API (Claude 3.x, Sonnet, Opus, Haiku)
|
|
5
|
+
using Anthropic's best practices for tool calling and JSON responses.
|
|
6
|
+
|
|
7
|
+
Supports three output modes for performance/reliability tradeoffs:
|
|
8
|
+
- strict: Use response_format for guaranteed schema compliance (slowest, 100% reliable)
|
|
9
|
+
- hint: Use prompt-based JSON instructions (medium speed, ~95% reliable)
|
|
10
|
+
- text: Plain text output for str return types (fastest)
|
|
11
|
+
|
|
12
|
+
Features:
|
|
13
|
+
- Automatic prompt caching for system messages (up to 90% cost reduction)
|
|
14
|
+
- Anti-XML tool calling instructions
|
|
15
|
+
- Output mode optimization based on return type
|
|
16
|
+
"""
|
|
17
|
+
|
|
18
|
+
import json
|
|
19
|
+
import logging
|
|
20
|
+
from typing import Any, Optional, get_args, get_origin
|
|
21
|
+
|
|
22
|
+
from pydantic import BaseModel
|
|
23
|
+
|
|
24
|
+
from .base_provider_handler import BaseProviderHandler
|
|
25
|
+
|
|
26
|
+
logger = logging.getLogger(__name__)
|
|
27
|
+
|
|
28
|
+
# Output mode constants
|
|
29
|
+
OUTPUT_MODE_STRICT = "strict"
|
|
30
|
+
OUTPUT_MODE_HINT = "hint"
|
|
31
|
+
OUTPUT_MODE_TEXT = "text"
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
class ClaudeHandler(BaseProviderHandler):
|
|
35
|
+
"""
|
|
36
|
+
Provider handler for Claude/Anthropic models.
|
|
37
|
+
|
|
38
|
+
Claude Characteristics:
|
|
39
|
+
- Excellent at following detailed instructions
|
|
40
|
+
- Native structured output via response_format (requires strict schema)
|
|
41
|
+
- Native tool calling (via Anthropic messages API)
|
|
42
|
+
- Performs best with anti-XML tool calling instructions
|
|
43
|
+
- Automatic prompt caching for cost optimization
|
|
44
|
+
|
|
45
|
+
Output Modes:
|
|
46
|
+
- strict: response_format with JSON schema (slowest, guaranteed valid JSON)
|
|
47
|
+
- hint: JSON schema in prompt (medium speed, usually valid JSON)
|
|
48
|
+
- text: Plain text output for str return types (fastest)
|
|
49
|
+
|
|
50
|
+
Best Practices (from Anthropic docs):
|
|
51
|
+
- Use response_format for guaranteed JSON schema compliance
|
|
52
|
+
- Schema must have additionalProperties: false on all objects
|
|
53
|
+
- Add anti-XML instructions to prevent <invoke> style tool calls
|
|
54
|
+
- Use one tool call at a time for better reliability
|
|
55
|
+
- Use cache_control for system prompts to reduce costs
|
|
56
|
+
"""
|
|
57
|
+
|
|
58
|
+
def __init__(self):
|
|
59
|
+
"""Initialize Claude handler."""
|
|
60
|
+
super().__init__(vendor="anthropic")
|
|
61
|
+
|
|
62
|
+
def _is_simple_schema(self, model_class: type[BaseModel]) -> bool:
|
|
63
|
+
"""
|
|
64
|
+
Check if a Pydantic model has a simple schema.
|
|
65
|
+
|
|
66
|
+
Simple schema criteria:
|
|
67
|
+
- Less than 5 fields
|
|
68
|
+
- All fields are basic types (str, int, float, bool, list, Optional)
|
|
69
|
+
- No nested Pydantic models
|
|
70
|
+
|
|
71
|
+
Args:
|
|
72
|
+
model_class: Pydantic model class
|
|
73
|
+
|
|
74
|
+
Returns:
|
|
75
|
+
True if schema is simple, False otherwise
|
|
76
|
+
"""
|
|
77
|
+
try:
|
|
78
|
+
schema = model_class.model_json_schema()
|
|
79
|
+
properties = schema.get("properties", {})
|
|
80
|
+
|
|
81
|
+
# Check field count
|
|
82
|
+
if len(properties) >= 5:
|
|
83
|
+
return False
|
|
84
|
+
|
|
85
|
+
# Check for nested objects or complex types
|
|
86
|
+
for field_name, field_schema in properties.items():
|
|
87
|
+
field_type = field_schema.get("type")
|
|
88
|
+
|
|
89
|
+
# Check for nested objects (indicates nested Pydantic model)
|
|
90
|
+
if field_type == "object" and "properties" in field_schema:
|
|
91
|
+
return False
|
|
92
|
+
|
|
93
|
+
# Check for $ref (nested model reference)
|
|
94
|
+
if "$ref" in field_schema:
|
|
95
|
+
return False
|
|
96
|
+
|
|
97
|
+
# Check array items for complex types
|
|
98
|
+
if field_type == "array":
|
|
99
|
+
items = field_schema.get("items", {})
|
|
100
|
+
if items.get("type") == "object" or "$ref" in items:
|
|
101
|
+
return False
|
|
102
|
+
|
|
103
|
+
return True
|
|
104
|
+
except Exception:
|
|
105
|
+
return False
|
|
106
|
+
|
|
107
|
+
def determine_output_mode(
|
|
108
|
+
self, output_type: type, override_mode: Optional[str] = None
|
|
109
|
+
) -> str:
|
|
110
|
+
"""
|
|
111
|
+
Determine the output mode based on return type.
|
|
112
|
+
|
|
113
|
+
Logic:
|
|
114
|
+
- If override_mode specified, use it
|
|
115
|
+
- If return type is str, use "text" mode
|
|
116
|
+
- If return type is simple schema (<5 fields, basic types), use "hint" mode
|
|
117
|
+
- Otherwise, use "strict" mode
|
|
118
|
+
|
|
119
|
+
Args:
|
|
120
|
+
output_type: Return type (str or BaseModel subclass)
|
|
121
|
+
override_mode: Optional override ("strict", "hint", or "text")
|
|
122
|
+
|
|
123
|
+
Returns:
|
|
124
|
+
Output mode string
|
|
125
|
+
"""
|
|
126
|
+
# Allow explicit override
|
|
127
|
+
if override_mode:
|
|
128
|
+
return override_mode
|
|
129
|
+
|
|
130
|
+
# String return type -> text mode
|
|
131
|
+
if output_type is str:
|
|
132
|
+
return OUTPUT_MODE_TEXT
|
|
133
|
+
|
|
134
|
+
# Check if it's a Pydantic model
|
|
135
|
+
if isinstance(output_type, type) and issubclass(output_type, BaseModel):
|
|
136
|
+
if self._is_simple_schema(output_type):
|
|
137
|
+
return OUTPUT_MODE_HINT
|
|
138
|
+
else:
|
|
139
|
+
return OUTPUT_MODE_STRICT
|
|
140
|
+
|
|
141
|
+
# Default to strict for unknown types
|
|
142
|
+
return OUTPUT_MODE_STRICT
|
|
143
|
+
|
|
144
|
+
def _make_schema_strict(self, schema: dict[str, Any]) -> dict[str, Any]:
|
|
145
|
+
"""
|
|
146
|
+
Make a JSON schema strict for Claude's structured output.
|
|
147
|
+
|
|
148
|
+
Claude requires additionalProperties: false on all object types.
|
|
149
|
+
This recursively processes the schema to add this constraint.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
schema: JSON schema dict
|
|
153
|
+
|
|
154
|
+
Returns:
|
|
155
|
+
Schema with additionalProperties: false on all objects
|
|
156
|
+
"""
|
|
157
|
+
if not isinstance(schema, dict):
|
|
158
|
+
return schema
|
|
159
|
+
|
|
160
|
+
result = schema.copy()
|
|
161
|
+
|
|
162
|
+
# If this is an object type, add additionalProperties: false
|
|
163
|
+
if result.get("type") == "object":
|
|
164
|
+
result["additionalProperties"] = False
|
|
165
|
+
|
|
166
|
+
# Recursively process nested schemas
|
|
167
|
+
if "properties" in result:
|
|
168
|
+
result["properties"] = {
|
|
169
|
+
k: self._make_schema_strict(v) for k, v in result["properties"].items()
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
# Process $defs (Pydantic uses this for nested models)
|
|
173
|
+
if "$defs" in result:
|
|
174
|
+
result["$defs"] = {
|
|
175
|
+
k: self._make_schema_strict(v) for k, v in result["$defs"].items()
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
# Process items for arrays
|
|
179
|
+
if "items" in result:
|
|
180
|
+
result["items"] = self._make_schema_strict(result["items"])
|
|
181
|
+
|
|
182
|
+
# Process anyOf, oneOf, allOf
|
|
183
|
+
for key in ["anyOf", "oneOf", "allOf"]:
|
|
184
|
+
if key in result:
|
|
185
|
+
result[key] = [self._make_schema_strict(s) for s in result[key]]
|
|
186
|
+
|
|
187
|
+
return result
|
|
188
|
+
|
|
189
|
+
def _apply_prompt_caching(
|
|
190
|
+
self, messages: list[dict[str, Any]]
|
|
191
|
+
) -> list[dict[str, Any]]:
|
|
192
|
+
"""
|
|
193
|
+
Apply prompt caching to system messages for Claude.
|
|
194
|
+
|
|
195
|
+
Claude's prompt caching feature caches the system prompt prefix,
|
|
196
|
+
reducing costs by up to 90% and improving latency for repeated calls.
|
|
197
|
+
|
|
198
|
+
The cache_control with type "ephemeral" tells Claude to cache
|
|
199
|
+
this content for the duration of the session (typically 5 minutes).
|
|
200
|
+
|
|
201
|
+
Args:
|
|
202
|
+
messages: List of message dicts
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
Messages with cache_control applied to system messages
|
|
206
|
+
|
|
207
|
+
Reference:
|
|
208
|
+
https://docs.anthropic.com/en/docs/build-with-claude/prompt-caching
|
|
209
|
+
"""
|
|
210
|
+
cached_messages = []
|
|
211
|
+
|
|
212
|
+
for msg in messages:
|
|
213
|
+
if msg.get("role") == "system":
|
|
214
|
+
content = msg.get("content", "")
|
|
215
|
+
|
|
216
|
+
# Convert string content to cached content block format
|
|
217
|
+
if isinstance(content, str):
|
|
218
|
+
cached_msg = {
|
|
219
|
+
"role": "system",
|
|
220
|
+
"content": [
|
|
221
|
+
{
|
|
222
|
+
"type": "text",
|
|
223
|
+
"text": content,
|
|
224
|
+
"cache_control": {"type": "ephemeral"},
|
|
225
|
+
}
|
|
226
|
+
],
|
|
227
|
+
}
|
|
228
|
+
cached_messages.append(cached_msg)
|
|
229
|
+
logger.debug(
|
|
230
|
+
f"🗄️ Applied prompt caching to system message ({len(content)} chars)"
|
|
231
|
+
)
|
|
232
|
+
elif isinstance(content, list):
|
|
233
|
+
# Already in content block format - add cache_control to last block
|
|
234
|
+
cached_content = []
|
|
235
|
+
for i, block in enumerate(content):
|
|
236
|
+
if isinstance(block, dict):
|
|
237
|
+
block_copy = block.copy()
|
|
238
|
+
# Add cache_control to the last text block
|
|
239
|
+
if i == len(content) - 1 and block.get("type") == "text":
|
|
240
|
+
block_copy["cache_control"] = {"type": "ephemeral"}
|
|
241
|
+
cached_content.append(block_copy)
|
|
242
|
+
else:
|
|
243
|
+
cached_content.append(block)
|
|
244
|
+
cached_messages.append(
|
|
245
|
+
{"role": "system", "content": cached_content}
|
|
246
|
+
)
|
|
247
|
+
logger.debug("🗄️ Applied prompt caching to system content blocks")
|
|
248
|
+
else:
|
|
249
|
+
# Unknown format - pass through unchanged
|
|
250
|
+
cached_messages.append(msg)
|
|
251
|
+
else:
|
|
252
|
+
# Non-system messages pass through unchanged
|
|
253
|
+
cached_messages.append(msg)
|
|
254
|
+
|
|
255
|
+
return cached_messages
|
|
256
|
+
|
|
257
|
+
def prepare_request(
|
|
258
|
+
self,
|
|
259
|
+
messages: list[dict[str, Any]],
|
|
260
|
+
tools: Optional[list[dict[str, Any]]],
|
|
261
|
+
output_type: type,
|
|
262
|
+
**kwargs: Any,
|
|
263
|
+
) -> dict[str, Any]:
|
|
264
|
+
"""
|
|
265
|
+
Prepare request parameters for Claude API with output mode support.
|
|
266
|
+
|
|
267
|
+
Output Mode Strategy:
|
|
268
|
+
- strict: Use response_format for guaranteed JSON schema compliance (slowest)
|
|
269
|
+
- hint: No response_format, rely on prompt instructions (medium speed)
|
|
270
|
+
- text: No response_format, plain text output (fastest)
|
|
271
|
+
|
|
272
|
+
Args:
|
|
273
|
+
messages: List of message dicts
|
|
274
|
+
tools: Optional list of tool schemas
|
|
275
|
+
output_type: Return type (str or Pydantic model)
|
|
276
|
+
**kwargs: Additional model parameters (may include output_mode override)
|
|
277
|
+
|
|
278
|
+
Returns:
|
|
279
|
+
Dictionary of parameters for litellm.completion()
|
|
280
|
+
"""
|
|
281
|
+
# Extract output_mode from kwargs if provided
|
|
282
|
+
output_mode = kwargs.pop("output_mode", None)
|
|
283
|
+
determined_mode = self.determine_output_mode(output_type, output_mode)
|
|
284
|
+
|
|
285
|
+
# Remove response_format from kwargs - we control this based on output mode
|
|
286
|
+
# The decorator's response_format="json" is just a hint for parsing, not API param
|
|
287
|
+
kwargs.pop("response_format", None)
|
|
288
|
+
|
|
289
|
+
# Apply prompt caching to system messages for cost optimization
|
|
290
|
+
cached_messages = self._apply_prompt_caching(messages)
|
|
291
|
+
|
|
292
|
+
request_params = {
|
|
293
|
+
"messages": cached_messages,
|
|
294
|
+
**kwargs, # Pass through temperature, max_tokens, etc.
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
# Add tools if provided
|
|
298
|
+
# LiteLLM will convert OpenAI tool format to Anthropic's format
|
|
299
|
+
if tools:
|
|
300
|
+
request_params["tools"] = tools
|
|
301
|
+
|
|
302
|
+
# Only add response_format in "strict" mode
|
|
303
|
+
if determined_mode == OUTPUT_MODE_STRICT:
|
|
304
|
+
# Claude requires additionalProperties: false on all object types
|
|
305
|
+
if isinstance(output_type, type) and issubclass(output_type, BaseModel):
|
|
306
|
+
schema = output_type.model_json_schema()
|
|
307
|
+
strict_schema = self._make_schema_strict(schema)
|
|
308
|
+
request_params["response_format"] = {
|
|
309
|
+
"type": "json_schema",
|
|
310
|
+
"json_schema": {
|
|
311
|
+
"name": output_type.__name__,
|
|
312
|
+
"schema": strict_schema,
|
|
313
|
+
"strict": False, # Allow optional fields with defaults
|
|
314
|
+
},
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
return request_params
|
|
318
|
+
|
|
319
|
+
def format_system_prompt(
|
|
320
|
+
self,
|
|
321
|
+
base_prompt: str,
|
|
322
|
+
tool_schemas: Optional[list[dict[str, Any]]],
|
|
323
|
+
output_type: type,
|
|
324
|
+
output_mode: Optional[str] = None,
|
|
325
|
+
) -> str:
|
|
326
|
+
"""
|
|
327
|
+
Format system prompt for Claude with output mode support.
|
|
328
|
+
|
|
329
|
+
Output Mode Strategy:
|
|
330
|
+
- strict: Minimal JSON instructions (response_format handles schema)
|
|
331
|
+
- hint: Add detailed JSON schema instructions in prompt
|
|
332
|
+
- text: No JSON instructions (plain text output)
|
|
333
|
+
|
|
334
|
+
Args:
|
|
335
|
+
base_prompt: Base system prompt
|
|
336
|
+
tool_schemas: Optional tool schemas
|
|
337
|
+
output_type: Expected response type
|
|
338
|
+
output_mode: Optional override for output mode
|
|
339
|
+
|
|
340
|
+
Returns:
|
|
341
|
+
Formatted system prompt optimized for Claude
|
|
342
|
+
"""
|
|
343
|
+
system_content = base_prompt
|
|
344
|
+
determined_mode = self.determine_output_mode(output_type, output_mode)
|
|
345
|
+
|
|
346
|
+
# Add tool calling instructions if tools available
|
|
347
|
+
# These prevent Claude from using XML-style <invoke> syntax
|
|
348
|
+
if tool_schemas:
|
|
349
|
+
system_content += """
|
|
350
|
+
|
|
351
|
+
IMPORTANT TOOL CALLING RULES:
|
|
352
|
+
- You have access to tools that you can call to gather information
|
|
353
|
+
- Make ONE tool call at a time
|
|
354
|
+
- NEVER use XML-style syntax like <invoke name="tool_name"/>
|
|
355
|
+
- After receiving tool results, you can make additional calls if needed
|
|
356
|
+
- Once you have all needed information, provide your final response
|
|
357
|
+
"""
|
|
358
|
+
|
|
359
|
+
# Add output format instructions based on mode
|
|
360
|
+
if determined_mode == OUTPUT_MODE_TEXT:
|
|
361
|
+
# Text mode: No JSON instructions
|
|
362
|
+
pass
|
|
363
|
+
|
|
364
|
+
elif determined_mode == OUTPUT_MODE_STRICT:
|
|
365
|
+
# Strict mode: Minimal instructions (response_format handles schema)
|
|
366
|
+
if isinstance(output_type, type) and issubclass(output_type, BaseModel):
|
|
367
|
+
system_content += f"\n\nYour final response will be structured as JSON matching the {output_type.__name__} format."
|
|
368
|
+
|
|
369
|
+
elif determined_mode == OUTPUT_MODE_HINT:
|
|
370
|
+
# Hint mode: Add detailed JSON schema instructions
|
|
371
|
+
if isinstance(output_type, type) and issubclass(output_type, BaseModel):
|
|
372
|
+
schema = output_type.model_json_schema()
|
|
373
|
+
properties = schema.get("properties", {})
|
|
374
|
+
required = schema.get("required", [])
|
|
375
|
+
|
|
376
|
+
# Build human-readable schema description
|
|
377
|
+
field_descriptions = []
|
|
378
|
+
for field_name, field_schema in properties.items():
|
|
379
|
+
field_type = field_schema.get("type", "any")
|
|
380
|
+
is_required = field_name in required
|
|
381
|
+
req_marker = " (required)" if is_required else " (optional)"
|
|
382
|
+
desc = field_schema.get("description", "")
|
|
383
|
+
desc_text = f" - {desc}" if desc else ""
|
|
384
|
+
field_descriptions.append(
|
|
385
|
+
f" - {field_name}: {field_type}{req_marker}{desc_text}"
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
fields_text = "\n".join(field_descriptions)
|
|
389
|
+
system_content += f"""
|
|
390
|
+
|
|
391
|
+
RESPONSE FORMAT:
|
|
392
|
+
You MUST respond with valid JSON matching this schema:
|
|
393
|
+
{{
|
|
394
|
+
{fields_text}
|
|
395
|
+
}}
|
|
396
|
+
|
|
397
|
+
Example format:
|
|
398
|
+
{json.dumps({k: f"<{v.get('type', 'value')}>" for k, v in properties.items()}, indent=2)}
|
|
399
|
+
|
|
400
|
+
IMPORTANT: Respond ONLY with valid JSON. No markdown code fences, no preamble text."""
|
|
401
|
+
|
|
402
|
+
return system_content
|
|
403
|
+
|
|
404
|
+
def get_vendor_capabilities(self) -> dict[str, bool]:
|
|
405
|
+
"""
|
|
406
|
+
Return Claude-specific capabilities.
|
|
407
|
+
|
|
408
|
+
Returns:
|
|
409
|
+
Capability flags for Claude
|
|
410
|
+
"""
|
|
411
|
+
return {
|
|
412
|
+
"native_tool_calling": True, # Claude has native function calling
|
|
413
|
+
"structured_output": True, # Native response_format support via LiteLLM
|
|
414
|
+
"streaming": True, # Supports streaming
|
|
415
|
+
"vision": True, # Claude 3+ supports vision
|
|
416
|
+
"json_mode": True, # Native JSON mode via response_format
|
|
417
|
+
"prompt_caching": True, # Automatic system prompt caching for cost savings
|
|
418
|
+
}
|