mcp-mesh 0.8.0b9__py3-none-any.whl → 0.9.0b1__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.
- _mcp_mesh/__init__.py +1 -1
- _mcp_mesh/engine/dependency_injector.py +9 -0
- _mcp_mesh/engine/mesh_llm_agent.py +53 -24
- _mcp_mesh/engine/mesh_llm_agent_injector.py +210 -39
- _mcp_mesh/engine/provider_handlers/__init__.py +2 -0
- _mcp_mesh/engine/provider_handlers/base_provider_handler.py +177 -0
- _mcp_mesh/engine/provider_handlers/claude_handler.py +129 -102
- _mcp_mesh/engine/provider_handlers/gemini_handler.py +218 -48
- _mcp_mesh/engine/provider_handlers/generic_handler.py +31 -0
- _mcp_mesh/engine/provider_handlers/openai_handler.py +4 -1
- _mcp_mesh/pipeline/api_heartbeat/rust_api_heartbeat.py +5 -1
- _mcp_mesh/pipeline/mcp_heartbeat/rust_heartbeat.py +30 -1
- _mcp_mesh/tracing/trace_context_helper.py +5 -3
- {mcp_mesh-0.8.0b9.dist-info → mcp_mesh-0.9.0b1.dist-info}/METADATA +4 -2
- {mcp_mesh-0.8.0b9.dist-info → mcp_mesh-0.9.0b1.dist-info}/RECORD +19 -19
- mesh/decorators.py +174 -92
- mesh/helpers.py +47 -2
- {mcp_mesh-0.8.0b9.dist-info → mcp_mesh-0.9.0b1.dist-info}/WHEEL +0 -0
- {mcp_mesh-0.8.0b9.dist-info → mcp_mesh-0.9.0b1.dist-info}/licenses/LICENSE +0 -0
_mcp_mesh/__init__.py
CHANGED
|
@@ -348,6 +348,15 @@ class DependencyInjector:
|
|
|
348
348
|
logger.debug(f"🤖 Creating LLM injection wrapper for {function_id}")
|
|
349
349
|
return self._llm_injector.create_injection_wrapper(func, function_id)
|
|
350
350
|
|
|
351
|
+
def initialize_direct_llm_agents(self) -> None:
|
|
352
|
+
"""
|
|
353
|
+
Initialize LLM agents that use direct LiteLLM (no mesh delegation).
|
|
354
|
+
|
|
355
|
+
This should be called during agent startup to initialize agents that
|
|
356
|
+
don't need to wait for registry response.
|
|
357
|
+
"""
|
|
358
|
+
self._llm_injector.initialize_direct_llm_agents()
|
|
359
|
+
|
|
351
360
|
def create_injection_wrapper(
|
|
352
361
|
self, func: Callable, dependencies: list[str]
|
|
353
362
|
) -> Callable:
|
|
@@ -9,13 +9,17 @@ import json
|
|
|
9
9
|
import logging
|
|
10
10
|
import time
|
|
11
11
|
from pathlib import Path
|
|
12
|
-
from typing import Any,
|
|
12
|
+
from typing import Any, Literal, Optional, Union
|
|
13
13
|
|
|
14
14
|
from pydantic import BaseModel
|
|
15
15
|
|
|
16
16
|
from .llm_config import LLMConfig
|
|
17
|
-
from .llm_errors import (
|
|
18
|
-
|
|
17
|
+
from .llm_errors import (
|
|
18
|
+
LLMAPIError,
|
|
19
|
+
MaxIterationsError,
|
|
20
|
+
ResponseParseError,
|
|
21
|
+
ToolExecutionError,
|
|
22
|
+
)
|
|
19
23
|
from .provider_handlers import ProviderHandlerRegistry
|
|
20
24
|
from .response_parser import ResponseParser
|
|
21
25
|
from .tool_executor import ToolExecutor
|
|
@@ -23,8 +27,7 @@ from .tool_schema_builder import ToolSchemaBuilder
|
|
|
23
27
|
|
|
24
28
|
# Import Jinja2 for template rendering
|
|
25
29
|
try:
|
|
26
|
-
from jinja2 import
|
|
27
|
-
TemplateSyntaxError)
|
|
30
|
+
from jinja2 import Environment, FileSystemLoader, Template, TemplateSyntaxError
|
|
28
31
|
except ImportError:
|
|
29
32
|
Environment = None
|
|
30
33
|
FileSystemLoader = None
|
|
@@ -536,8 +539,8 @@ IMPORTANT TOOL CALLING RULES:
|
|
|
536
539
|
|
|
537
540
|
logger.debug(
|
|
538
541
|
f"📥 Received response from mesh provider: "
|
|
539
|
-
f"content={message_dict.get('content'
|
|
540
|
-
f"tool_calls={len(message_dict.get('tool_calls'
|
|
542
|
+
f"content={(message_dict.get('content') or '')[:200]}..., "
|
|
543
|
+
f"tool_calls={len(message_dict.get('tool_calls') or [])}"
|
|
541
544
|
)
|
|
542
545
|
|
|
543
546
|
return MockResponse(message_dict)
|
|
@@ -615,13 +618,20 @@ IMPORTANT TOOL CALLING RULES:
|
|
|
615
618
|
# Render base system prompt (from template or literal) with effective context
|
|
616
619
|
base_system_prompt = self._render_system_prompt(effective_context)
|
|
617
620
|
|
|
618
|
-
# Phase 2:
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
621
|
+
# Phase 2: Format system prompt
|
|
622
|
+
if self._is_mesh_delegated:
|
|
623
|
+
# Delegate path: Just use base prompt + basic tool instructions
|
|
624
|
+
# Provider will add vendor-specific formatting
|
|
625
|
+
system_content = base_system_prompt
|
|
626
|
+
if self._tool_schemas:
|
|
627
|
+
system_content += "\n\nYou have access to tools. Use them when needed to gather information."
|
|
628
|
+
else:
|
|
629
|
+
# Direct path: Use vendor handler for vendor-specific optimizations
|
|
630
|
+
system_content = self._provider_handler.format_system_prompt(
|
|
631
|
+
base_prompt=base_system_prompt,
|
|
632
|
+
tool_schemas=self._tool_schemas,
|
|
633
|
+
output_type=self.output_type,
|
|
634
|
+
)
|
|
625
635
|
|
|
626
636
|
# Debug: Log system prompt (truncated for privacy)
|
|
627
637
|
logger.debug(
|
|
@@ -633,12 +643,14 @@ IMPORTANT TOOL CALLING RULES:
|
|
|
633
643
|
# Multi-turn conversation - use provided messages array
|
|
634
644
|
messages = message.copy()
|
|
635
645
|
|
|
636
|
-
#
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
646
|
+
# Only add/update system message if we have non-empty content
|
|
647
|
+
# (Claude API rejects empty system messages - though decorator provides default)
|
|
648
|
+
if system_content:
|
|
649
|
+
if not messages or messages[0].get("role") != "system":
|
|
650
|
+
messages.insert(0, {"role": "system", "content": system_content})
|
|
651
|
+
else:
|
|
652
|
+
# Replace existing system message with our constructed one
|
|
653
|
+
messages[0] = {"role": "system", "content": system_content}
|
|
642
654
|
|
|
643
655
|
# Log conversation history
|
|
644
656
|
logger.info(
|
|
@@ -646,10 +658,17 @@ IMPORTANT TOOL CALLING RULES:
|
|
|
646
658
|
)
|
|
647
659
|
else:
|
|
648
660
|
# Single-turn - build messages array from string
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
661
|
+
# Only include system message if non-empty (Claude API rejects empty system messages)
|
|
662
|
+
if system_content:
|
|
663
|
+
messages = [
|
|
664
|
+
{"role": "system", "content": system_content},
|
|
665
|
+
{"role": "user", "content": message},
|
|
666
|
+
]
|
|
667
|
+
else:
|
|
668
|
+
# Fallback for edge case where system_content is explicitly empty
|
|
669
|
+
messages = [
|
|
670
|
+
{"role": "user", "content": message},
|
|
671
|
+
]
|
|
653
672
|
|
|
654
673
|
logger.info(f"🚀 Starting agentic loop for message: {message[:100]}...")
|
|
655
674
|
|
|
@@ -705,6 +724,16 @@ IMPORTANT TOOL CALLING RULES:
|
|
|
705
724
|
if self.model:
|
|
706
725
|
model_params["model"] = self.model
|
|
707
726
|
|
|
727
|
+
# Issue #459: Include output_schema for provider to apply vendor-specific handling
|
|
728
|
+
# (e.g., OpenAI needs response_format, not prompt-based JSON instructions)
|
|
729
|
+
if self.output_type is not str and hasattr(
|
|
730
|
+
self.output_type, "model_json_schema"
|
|
731
|
+
):
|
|
732
|
+
model_params["output_schema"] = (
|
|
733
|
+
self.output_type.model_json_schema()
|
|
734
|
+
)
|
|
735
|
+
model_params["output_type_name"] = self.output_type.__name__
|
|
736
|
+
|
|
708
737
|
logger.debug(
|
|
709
738
|
f"📤 Delegating to mesh provider with handler-prepared params: "
|
|
710
739
|
f"keys={list(model_params.keys())}"
|
|
@@ -65,6 +65,78 @@ class MeshLlmAgentInjector(BaseInjector):
|
|
|
65
65
|
super().__init__()
|
|
66
66
|
self._llm_agents: dict[str, dict[str, Any]] = {}
|
|
67
67
|
|
|
68
|
+
def initialize_direct_llm_agents(self) -> None:
|
|
69
|
+
"""
|
|
70
|
+
Initialize LLM agents that use direct LiteLLM (no mesh delegation).
|
|
71
|
+
|
|
72
|
+
This handles the case where:
|
|
73
|
+
- provider is a string (e.g., "claude") - direct LiteLLM call
|
|
74
|
+
- filter is None or empty - no mesh tools needed
|
|
75
|
+
|
|
76
|
+
These agents don't need to wait for registry response since all
|
|
77
|
+
information is available at decorator time.
|
|
78
|
+
"""
|
|
79
|
+
llm_agents = DecoratorRegistry.get_mesh_llm_agents()
|
|
80
|
+
|
|
81
|
+
for function_id, llm_metadata in llm_agents.items():
|
|
82
|
+
config = llm_metadata.config
|
|
83
|
+
provider = config.get("provider")
|
|
84
|
+
filter_config = config.get("filter")
|
|
85
|
+
|
|
86
|
+
# Check if this is a direct LiteLLM agent (provider is string, not dict)
|
|
87
|
+
is_direct_llm = isinstance(provider, str)
|
|
88
|
+
|
|
89
|
+
# Check if no tools needed (filter is None or empty)
|
|
90
|
+
has_no_filter = filter_config is None or (
|
|
91
|
+
isinstance(filter_config, list) and len(filter_config) == 0
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
if is_direct_llm and has_no_filter:
|
|
95
|
+
# Skip if already initialized
|
|
96
|
+
if function_id in self._llm_agents:
|
|
97
|
+
continue
|
|
98
|
+
|
|
99
|
+
logger.info(
|
|
100
|
+
f"🔧 Initializing direct LiteLLM agent for '{function_id}' "
|
|
101
|
+
f"(provider={provider}, no filter)"
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
# Initialize empty tools data for direct LiteLLM
|
|
105
|
+
self._llm_agents[function_id] = {
|
|
106
|
+
"config": config,
|
|
107
|
+
"output_type": llm_metadata.output_type,
|
|
108
|
+
"param_name": llm_metadata.param_name,
|
|
109
|
+
"tools_metadata": [], # No tools for direct LiteLLM
|
|
110
|
+
"tools_proxies": {}, # No tool proxies needed
|
|
111
|
+
"function": llm_metadata.function,
|
|
112
|
+
"provider_proxy": None, # No mesh delegation
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
# Get the wrapper and update it with LLM agent
|
|
116
|
+
wrapper = llm_metadata.function
|
|
117
|
+
if wrapper and hasattr(wrapper, "_mesh_update_llm_agent"):
|
|
118
|
+
llm_agent = self._create_llm_agent(function_id)
|
|
119
|
+
wrapper._mesh_update_llm_agent(llm_agent)
|
|
120
|
+
logger.info(
|
|
121
|
+
f"🔄 Updated wrapper with MeshLlmAgent for '{function_id}' (direct LiteLLM mode)"
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# Set factory for per-call context agent creation (template support)
|
|
125
|
+
if config.get("is_template", False):
|
|
126
|
+
|
|
127
|
+
def create_context_agent(
|
|
128
|
+
context_value: Any, _func_id: str = function_id
|
|
129
|
+
) -> MeshLlmAgent:
|
|
130
|
+
"""Factory to create MeshLlmAgent with context for template rendering."""
|
|
131
|
+
return self._create_llm_agent(
|
|
132
|
+
_func_id, context_value=context_value
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
wrapper._mesh_create_context_agent = create_context_agent
|
|
136
|
+
logger.info(
|
|
137
|
+
f"🎯 Set context agent factory for template-based function '{function_id}' (direct LiteLLM mode)"
|
|
138
|
+
)
|
|
139
|
+
|
|
68
140
|
def _build_function_name_to_id_mapping(self) -> dict[str, str]:
|
|
69
141
|
"""
|
|
70
142
|
Build mapping from function_name to function_id.
|
|
@@ -176,16 +248,49 @@ class MeshLlmAgentInjector(BaseInjector):
|
|
|
176
248
|
f"✅ Set provider proxy for '{function_id}': {provider_proxy.function_name} at {provider_proxy.endpoint} (vendor={vendor})"
|
|
177
249
|
)
|
|
178
250
|
|
|
179
|
-
# Re-create and update MeshLlmAgent with new provider
|
|
180
|
-
# Get the function wrapper from DecoratorRegistry
|
|
251
|
+
# Re-create and update MeshLlmAgent with new provider
|
|
252
|
+
# Get the function wrapper and metadata from DecoratorRegistry
|
|
181
253
|
llm_agents = DecoratorRegistry.get_mesh_llm_agents()
|
|
182
254
|
wrapper = None
|
|
255
|
+
llm_metadata = None
|
|
183
256
|
for agent_func_id, metadata in llm_agents.items():
|
|
184
257
|
if metadata.function_id == function_id:
|
|
185
258
|
wrapper = metadata.function
|
|
259
|
+
llm_metadata = metadata
|
|
186
260
|
break
|
|
187
261
|
|
|
188
|
-
#
|
|
262
|
+
# Check if tools are required (filter is specified)
|
|
263
|
+
has_filter = False
|
|
264
|
+
if llm_metadata and llm_metadata.config:
|
|
265
|
+
filter_config = llm_metadata.config.get("filter")
|
|
266
|
+
has_filter = filter_config is not None and len(filter_config) > 0
|
|
267
|
+
|
|
268
|
+
# CRITICAL FIX: Always set config if not already set (prevents KeyError during race condition)
|
|
269
|
+
# When provider arrives before tools, config must be available for inject_llm_agent
|
|
270
|
+
# to create the LLM agent with context. Without this, _create_llm_agent fails with KeyError.
|
|
271
|
+
if "config" not in self._llm_agents[function_id] and llm_metadata:
|
|
272
|
+
self._llm_agents[function_id]["config"] = llm_metadata.config
|
|
273
|
+
self._llm_agents[function_id]["output_type"] = llm_metadata.output_type
|
|
274
|
+
self._llm_agents[function_id]["param_name"] = llm_metadata.param_name
|
|
275
|
+
self._llm_agents[function_id]["function"] = llm_metadata.function
|
|
276
|
+
logger.debug(
|
|
277
|
+
f"📋 Set config from DecoratorRegistry for '{function_id}' (awaiting tools)"
|
|
278
|
+
)
|
|
279
|
+
|
|
280
|
+
# If no filter specified, initialize empty tools data so we can create LLM agent without tools
|
|
281
|
+
# This supports simple LLM calls (text generation) that don't need tool calling
|
|
282
|
+
if not has_filter and "tools_metadata" not in self._llm_agents[function_id]:
|
|
283
|
+
self._llm_agents[function_id].update(
|
|
284
|
+
{
|
|
285
|
+
"tools_metadata": [], # No tools for simple LLM calls
|
|
286
|
+
"tools_proxies": {}, # No tool proxies needed
|
|
287
|
+
}
|
|
288
|
+
)
|
|
289
|
+
logger.info(
|
|
290
|
+
f"✅ Initialized empty tools for '{function_id}' (no filter specified - simple LLM mode)"
|
|
291
|
+
)
|
|
292
|
+
|
|
293
|
+
# Update wrapper if we have tools data (either from filter matching or initialized empty)
|
|
189
294
|
if (
|
|
190
295
|
wrapper
|
|
191
296
|
and hasattr(wrapper, "_mesh_update_llm_agent")
|
|
@@ -194,9 +299,26 @@ class MeshLlmAgentInjector(BaseInjector):
|
|
|
194
299
|
llm_agent = self._create_llm_agent(function_id)
|
|
195
300
|
wrapper._mesh_update_llm_agent(llm_agent)
|
|
196
301
|
logger.info(
|
|
197
|
-
f"🔄 Updated wrapper with
|
|
302
|
+
f"🔄 Updated wrapper with MeshLlmAgent for '{function_id}'"
|
|
303
|
+
+ (" (with tools)" if has_filter else " (simple LLM mode)")
|
|
198
304
|
)
|
|
199
|
-
|
|
305
|
+
|
|
306
|
+
# Set factory for per-call context agent creation (template support)
|
|
307
|
+
# This is critical for filter=None cases where _process_function_tools isn't called
|
|
308
|
+
config_dict = llm_metadata.config if llm_metadata else {}
|
|
309
|
+
if config_dict.get("is_template", False):
|
|
310
|
+
# Capture function_id by value using default argument to avoid closure issues
|
|
311
|
+
def create_context_agent(
|
|
312
|
+
context_value: Any, _func_id: str = function_id
|
|
313
|
+
) -> MeshLlmAgent:
|
|
314
|
+
"""Factory to create MeshLlmAgent with context for template rendering."""
|
|
315
|
+
return self._create_llm_agent(_func_id, context_value=context_value)
|
|
316
|
+
|
|
317
|
+
wrapper._mesh_create_context_agent = create_context_agent
|
|
318
|
+
logger.info(
|
|
319
|
+
f"🎯 Set context agent factory for template-based function '{function_id}' (simple LLM mode)"
|
|
320
|
+
)
|
|
321
|
+
elif wrapper and hasattr(wrapper, "_mesh_update_llm_agent") and has_filter:
|
|
200
322
|
logger.debug(
|
|
201
323
|
f"⏳ Provider set for '{function_id}', waiting for tools before updating wrapper"
|
|
202
324
|
)
|
|
@@ -432,36 +554,62 @@ class MeshLlmAgentInjector(BaseInjector):
|
|
|
432
554
|
def inject_llm_agent(func: Callable, args: tuple, kwargs: dict) -> tuple:
|
|
433
555
|
"""Inject LLM agent into kwargs if not provided."""
|
|
434
556
|
if param_name not in kwargs or kwargs.get(param_name) is None:
|
|
435
|
-
#
|
|
557
|
+
# Get config from runtime data or fallback to decorator registry.
|
|
558
|
+
# Runtime data (self._llm_agents) is populated during heartbeat and has
|
|
559
|
+
# tools/provider info. Decorator registry is populated at decorator time
|
|
560
|
+
# and always has config/context_param. For self-dependency calls that
|
|
561
|
+
# happen before heartbeat, we need the decorator registry fallback.
|
|
562
|
+
agent_data = None
|
|
563
|
+
config_dict = None
|
|
564
|
+
|
|
565
|
+
# Try runtime data first (has tools, provider from heartbeat)
|
|
436
566
|
if function_id in self._llm_agents:
|
|
437
567
|
agent_data = self._llm_agents[function_id]
|
|
438
|
-
config_dict = agent_data
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
func, explicit_name=context_param_name
|
|
568
|
+
config_dict = agent_data.get("config")
|
|
569
|
+
|
|
570
|
+
# Fallback to decorator registry (always available, has context_param)
|
|
571
|
+
# This is critical for self-dependency calls that happen before heartbeat
|
|
572
|
+
if config_dict is None:
|
|
573
|
+
llm_agents_registry = DecoratorRegistry.get_mesh_llm_agents()
|
|
574
|
+
if function_id in llm_agents_registry:
|
|
575
|
+
llm_metadata = llm_agents_registry[function_id]
|
|
576
|
+
config_dict = llm_metadata.config
|
|
577
|
+
logger.debug(
|
|
578
|
+
f"🔄 Using DecoratorRegistry fallback for '{function_id}' config (self-dependency before heartbeat)"
|
|
450
579
|
)
|
|
451
580
|
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
581
|
+
# Check if templates are enabled
|
|
582
|
+
is_template = (
|
|
583
|
+
config_dict.get("is_template", False) if config_dict else False
|
|
584
|
+
)
|
|
456
585
|
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
elif ctx_index < len(args):
|
|
462
|
-
context_value = args[ctx_index]
|
|
586
|
+
if is_template and config_dict:
|
|
587
|
+
# Templates enabled - create per-call agent with context
|
|
588
|
+
# Import signature analyzer for context detection
|
|
589
|
+
from .signature_analyzer import get_context_parameter_name
|
|
463
590
|
|
|
464
|
-
|
|
591
|
+
# Detect context parameter
|
|
592
|
+
context_param_name = config_dict.get("context_param")
|
|
593
|
+
context_info = get_context_parameter_name(
|
|
594
|
+
func, explicit_name=context_param_name
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
# Extract context value from call
|
|
598
|
+
context_value = None
|
|
599
|
+
if context_info is not None:
|
|
600
|
+
ctx_name, ctx_index = context_info
|
|
601
|
+
|
|
602
|
+
# Try kwargs first
|
|
603
|
+
if ctx_name in kwargs:
|
|
604
|
+
context_value = kwargs[ctx_name]
|
|
605
|
+
# Then try positional args
|
|
606
|
+
elif ctx_index < len(args):
|
|
607
|
+
context_value = args[ctx_index]
|
|
608
|
+
|
|
609
|
+
# Create agent with context for this call
|
|
610
|
+
# Note: _create_llm_agent requires function_id in self._llm_agents
|
|
611
|
+
# If not available yet, use cached agent with context_value set directly
|
|
612
|
+
if function_id in self._llm_agents:
|
|
465
613
|
current_agent = self._create_llm_agent(
|
|
466
614
|
function_id, context_value=context_value
|
|
467
615
|
)
|
|
@@ -469,22 +617,41 @@ class MeshLlmAgentInjector(BaseInjector):
|
|
|
469
617
|
f"🤖 Created MeshLlmAgent with context for {func.__name__}.{param_name}"
|
|
470
618
|
)
|
|
471
619
|
else:
|
|
472
|
-
#
|
|
620
|
+
# Runtime data not yet available - use cached agent but log warning
|
|
621
|
+
# The cached agent may have been created without context
|
|
473
622
|
current_agent = wrapper._mesh_llm_agent
|
|
474
623
|
if current_agent is not None:
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
624
|
+
# Update context on the cached agent if possible
|
|
625
|
+
if hasattr(current_agent, "_context_value"):
|
|
626
|
+
current_agent._context_value = context_value
|
|
627
|
+
logger.debug(
|
|
628
|
+
f"🤖 Updated context on cached MeshLlmAgent for {func.__name__}.{param_name}"
|
|
629
|
+
)
|
|
630
|
+
else:
|
|
631
|
+
logger.debug(
|
|
632
|
+
f"🤖 Injected cached MeshLlmAgent into {func.__name__}.{param_name} (context may not be applied)"
|
|
633
|
+
)
|
|
478
634
|
else:
|
|
479
635
|
logger.warning(
|
|
480
636
|
f"⚠️ MeshLlmAgent for {func.__name__}.{param_name} is None (tools not yet received from registry)"
|
|
481
637
|
)
|
|
638
|
+
elif config_dict:
|
|
639
|
+
# No template - use cached agent (existing behavior)
|
|
640
|
+
current_agent = wrapper._mesh_llm_agent
|
|
641
|
+
if current_agent is not None:
|
|
642
|
+
logger.debug(
|
|
643
|
+
f"🤖 Injected MeshLlmAgent into {func.__name__}.{param_name}"
|
|
644
|
+
)
|
|
645
|
+
else:
|
|
646
|
+
logger.warning(
|
|
647
|
+
f"⚠️ MeshLlmAgent for {func.__name__}.{param_name} is None (tools not yet received from registry)"
|
|
648
|
+
)
|
|
482
649
|
else:
|
|
483
|
-
# No
|
|
650
|
+
# No config found anywhere - use cached (backward compatibility)
|
|
484
651
|
current_agent = wrapper._mesh_llm_agent
|
|
485
652
|
if current_agent is None:
|
|
486
653
|
logger.warning(
|
|
487
|
-
f"⚠️ MeshLlmAgent for {func.__name__}.{param_name} is None (
|
|
654
|
+
f"⚠️ MeshLlmAgent for {func.__name__}.{param_name} is None (no config found)"
|
|
488
655
|
)
|
|
489
656
|
|
|
490
657
|
kwargs[param_name] = current_agent
|
|
@@ -585,13 +752,17 @@ class MeshLlmAgentInjector(BaseInjector):
|
|
|
585
752
|
)
|
|
586
753
|
|
|
587
754
|
# Create MeshLlmAgent with both metadata and proxies
|
|
755
|
+
# Use .get() with defaults for tools_metadata/proxies to handle race condition
|
|
756
|
+
# where provider arrives before tools (filter-based agents)
|
|
588
757
|
llm_agent = MeshLlmAgent(
|
|
589
758
|
config=llm_config,
|
|
590
|
-
filtered_tools=llm_agent_data
|
|
591
|
-
"tools_metadata"
|
|
592
|
-
|
|
759
|
+
filtered_tools=llm_agent_data.get(
|
|
760
|
+
"tools_metadata", []
|
|
761
|
+
), # Metadata for schema building (empty if tools not yet received)
|
|
593
762
|
output_type=llm_agent_data["output_type"],
|
|
594
|
-
tool_proxies=llm_agent_data
|
|
763
|
+
tool_proxies=llm_agent_data.get(
|
|
764
|
+
"tools_proxies", {}
|
|
765
|
+
), # Proxies for execution (empty if tools not yet received)
|
|
595
766
|
template_path=template_path if is_template else None,
|
|
596
767
|
context_value=context_value if is_template else None,
|
|
597
768
|
provider_proxy=llm_agent_data.get(
|
|
@@ -9,6 +9,7 @@ from .base_provider_handler import (
|
|
|
9
9
|
BASE_TOOL_INSTRUCTIONS,
|
|
10
10
|
CLAUDE_ANTI_XML_INSTRUCTION,
|
|
11
11
|
BaseProviderHandler,
|
|
12
|
+
is_simple_schema,
|
|
12
13
|
make_schema_strict,
|
|
13
14
|
)
|
|
14
15
|
from .claude_handler import ClaudeHandler
|
|
@@ -22,6 +23,7 @@ __all__ = [
|
|
|
22
23
|
"BASE_TOOL_INSTRUCTIONS",
|
|
23
24
|
"CLAUDE_ANTI_XML_INSTRUCTION",
|
|
24
25
|
# Utilities
|
|
26
|
+
"is_simple_schema",
|
|
25
27
|
"make_schema_strict",
|
|
26
28
|
# Handlers
|
|
27
29
|
"BaseProviderHandler",
|