letta-nightly 0.11.7.dev20251006104136__py3-none-any.whl → 0.11.7.dev20251008104128__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.
- letta/adapters/letta_llm_adapter.py +1 -0
- letta/adapters/letta_llm_request_adapter.py +0 -1
- letta/adapters/letta_llm_stream_adapter.py +7 -2
- letta/adapters/simple_llm_request_adapter.py +88 -0
- letta/adapters/simple_llm_stream_adapter.py +192 -0
- letta/agents/agent_loop.py +6 -0
- letta/agents/ephemeral_summary_agent.py +2 -1
- letta/agents/helpers.py +142 -6
- letta/agents/letta_agent.py +13 -33
- letta/agents/letta_agent_batch.py +2 -4
- letta/agents/letta_agent_v2.py +87 -77
- letta/agents/letta_agent_v3.py +899 -0
- letta/agents/voice_agent.py +2 -6
- letta/constants.py +8 -4
- letta/errors.py +40 -0
- letta/functions/function_sets/base.py +84 -4
- letta/functions/function_sets/multi_agent.py +0 -3
- letta/functions/schema_generator.py +113 -71
- letta/groups/dynamic_multi_agent.py +3 -2
- letta/groups/helpers.py +1 -2
- letta/groups/round_robin_multi_agent.py +3 -2
- letta/groups/sleeptime_multi_agent.py +3 -2
- letta/groups/sleeptime_multi_agent_v2.py +1 -1
- letta/groups/sleeptime_multi_agent_v3.py +17 -17
- letta/groups/supervisor_multi_agent.py +84 -80
- letta/helpers/converters.py +3 -0
- letta/helpers/message_helper.py +4 -0
- letta/helpers/tool_rule_solver.py +92 -5
- letta/interfaces/anthropic_streaming_interface.py +409 -0
- letta/interfaces/gemini_streaming_interface.py +296 -0
- letta/interfaces/openai_streaming_interface.py +752 -1
- letta/llm_api/anthropic_client.py +126 -16
- letta/llm_api/bedrock_client.py +4 -2
- letta/llm_api/deepseek_client.py +4 -1
- letta/llm_api/google_vertex_client.py +123 -42
- letta/llm_api/groq_client.py +4 -1
- letta/llm_api/llm_api_tools.py +11 -4
- letta/llm_api/llm_client_base.py +6 -2
- letta/llm_api/openai.py +32 -2
- letta/llm_api/openai_client.py +423 -18
- letta/llm_api/xai_client.py +4 -1
- letta/main.py +9 -5
- letta/memory.py +1 -0
- letta/orm/__init__.py +1 -1
- letta/orm/agent.py +10 -0
- letta/orm/block.py +7 -16
- letta/orm/blocks_agents.py +8 -2
- letta/orm/files_agents.py +2 -0
- letta/orm/job.py +7 -5
- letta/orm/mcp_oauth.py +1 -0
- letta/orm/message.py +21 -6
- letta/orm/organization.py +2 -0
- letta/orm/provider.py +6 -2
- letta/orm/run.py +71 -0
- letta/orm/sandbox_config.py +7 -1
- letta/orm/sqlalchemy_base.py +0 -306
- letta/orm/step.py +6 -5
- letta/orm/step_metrics.py +5 -5
- letta/otel/tracing.py +28 -3
- letta/plugins/defaults.py +4 -4
- letta/prompts/system_prompts/__init__.py +2 -0
- letta/prompts/system_prompts/letta_v1.py +25 -0
- letta/schemas/agent.py +3 -2
- letta/schemas/agent_file.py +9 -3
- letta/schemas/block.py +23 -10
- letta/schemas/enums.py +21 -2
- letta/schemas/job.py +17 -4
- letta/schemas/letta_message_content.py +71 -2
- letta/schemas/letta_stop_reason.py +5 -5
- letta/schemas/llm_config.py +53 -3
- letta/schemas/memory.py +1 -1
- letta/schemas/message.py +504 -117
- letta/schemas/openai/responses_request.py +64 -0
- letta/schemas/providers/__init__.py +2 -0
- letta/schemas/providers/anthropic.py +16 -0
- letta/schemas/providers/ollama.py +115 -33
- letta/schemas/providers/openrouter.py +52 -0
- letta/schemas/providers/vllm.py +2 -1
- letta/schemas/run.py +48 -42
- letta/schemas/step.py +2 -2
- letta/schemas/step_metrics.py +1 -1
- letta/schemas/tool.py +15 -107
- letta/schemas/tool_rule.py +88 -5
- letta/serialize_schemas/marshmallow_agent.py +1 -0
- letta/server/db.py +86 -408
- letta/server/rest_api/app.py +61 -10
- letta/server/rest_api/dependencies.py +14 -0
- letta/server/rest_api/redis_stream_manager.py +19 -8
- letta/server/rest_api/routers/v1/agents.py +364 -292
- letta/server/rest_api/routers/v1/blocks.py +14 -20
- letta/server/rest_api/routers/v1/identities.py +45 -110
- letta/server/rest_api/routers/v1/internal_templates.py +21 -0
- letta/server/rest_api/routers/v1/jobs.py +23 -6
- letta/server/rest_api/routers/v1/messages.py +1 -1
- letta/server/rest_api/routers/v1/runs.py +126 -85
- letta/server/rest_api/routers/v1/sandbox_configs.py +10 -19
- letta/server/rest_api/routers/v1/tools.py +281 -594
- letta/server/rest_api/routers/v1/voice.py +1 -1
- letta/server/rest_api/streaming_response.py +29 -29
- letta/server/rest_api/utils.py +122 -64
- letta/server/server.py +160 -887
- letta/services/agent_manager.py +236 -919
- letta/services/agent_serialization_manager.py +16 -0
- letta/services/archive_manager.py +0 -100
- letta/services/block_manager.py +211 -168
- letta/services/file_manager.py +1 -1
- letta/services/files_agents_manager.py +24 -33
- letta/services/group_manager.py +0 -142
- letta/services/helpers/agent_manager_helper.py +7 -2
- letta/services/helpers/run_manager_helper.py +85 -0
- letta/services/job_manager.py +96 -411
- letta/services/lettuce/__init__.py +6 -0
- letta/services/lettuce/lettuce_client_base.py +86 -0
- letta/services/mcp_manager.py +38 -6
- letta/services/message_manager.py +165 -362
- letta/services/organization_manager.py +0 -36
- letta/services/passage_manager.py +0 -345
- letta/services/provider_manager.py +0 -80
- letta/services/run_manager.py +301 -0
- letta/services/sandbox_config_manager.py +0 -234
- letta/services/step_manager.py +62 -39
- letta/services/summarizer/summarizer.py +9 -7
- letta/services/telemetry_manager.py +0 -16
- letta/services/tool_executor/builtin_tool_executor.py +35 -0
- letta/services/tool_executor/core_tool_executor.py +397 -2
- letta/services/tool_executor/files_tool_executor.py +3 -3
- letta/services/tool_executor/multi_agent_tool_executor.py +30 -15
- letta/services/tool_executor/tool_execution_manager.py +6 -8
- letta/services/tool_executor/tool_executor_base.py +3 -3
- letta/services/tool_manager.py +85 -339
- letta/services/tool_sandbox/base.py +24 -13
- letta/services/tool_sandbox/e2b_sandbox.py +16 -1
- letta/services/tool_schema_generator.py +123 -0
- letta/services/user_manager.py +0 -99
- letta/settings.py +20 -4
- {letta_nightly-0.11.7.dev20251006104136.dist-info → letta_nightly-0.11.7.dev20251008104128.dist-info}/METADATA +3 -5
- {letta_nightly-0.11.7.dev20251006104136.dist-info → letta_nightly-0.11.7.dev20251008104128.dist-info}/RECORD +140 -132
- letta/agents/temporal/activities/__init__.py +0 -4
- letta/agents/temporal/activities/example_activity.py +0 -7
- letta/agents/temporal/activities/prepare_messages.py +0 -10
- letta/agents/temporal/temporal_agent_workflow.py +0 -56
- letta/agents/temporal/types.py +0 -25
- {letta_nightly-0.11.7.dev20251006104136.dist-info → letta_nightly-0.11.7.dev20251008104128.dist-info}/WHEEL +0 -0
- {letta_nightly-0.11.7.dev20251006104136.dist-info → letta_nightly-0.11.7.dev20251008104128.dist-info}/entry_points.txt +0 -0
- {letta_nightly-0.11.7.dev20251006104136.dist-info → letta_nightly-0.11.7.dev20251008104128.dist-info}/licenses/LICENSE +0 -0
letta/schemas/tool.py
CHANGED
@@ -3,7 +3,6 @@ from typing import Any, Dict, List, Optional
|
|
3
3
|
from pydantic import ConfigDict, Field, model_validator
|
4
4
|
|
5
5
|
from letta.constants import (
|
6
|
-
COMPOSIO_TOOL_TAG_NAME,
|
7
6
|
FUNCTION_RETURN_CHAR_LIMIT,
|
8
7
|
LETTA_BUILTIN_TOOL_MODULE_NAME,
|
9
8
|
LETTA_CORE_TOOL_MODULE_NAME,
|
@@ -16,15 +15,9 @@ from letta.constants import (
|
|
16
15
|
# MCP Tool metadata constants for schema health status
|
17
16
|
MCP_TOOL_METADATA_SCHEMA_STATUS = f"{MCP_TOOL_TAG_NAME_PREFIX}:SCHEMA_STATUS"
|
18
17
|
MCP_TOOL_METADATA_SCHEMA_WARNINGS = f"{MCP_TOOL_TAG_NAME_PREFIX}:SCHEMA_WARNINGS"
|
19
|
-
from letta.functions.
|
20
|
-
from letta.functions.composio_helpers import generate_composio_tool_wrapper
|
21
|
-
from letta.functions.functions import derive_openai_json_schema, get_json_schema_from_module
|
18
|
+
from letta.functions.functions import get_json_schema_from_module
|
22
19
|
from letta.functions.mcp_client.types import MCPTool
|
23
|
-
from letta.functions.schema_generator import
|
24
|
-
generate_schema_from_args_schema_v2,
|
25
|
-
generate_tool_schema_for_composio,
|
26
|
-
generate_tool_schema_for_mcp,
|
27
|
-
)
|
20
|
+
from letta.functions.schema_generator import generate_tool_schema_for_mcp
|
28
21
|
from letta.log import get_logger
|
29
22
|
from letta.schemas.enums import ToolSourceType, ToolType
|
30
23
|
from letta.schemas.letta_base import LettaBase
|
@@ -80,46 +73,19 @@ class Tool(BaseTool):
|
|
80
73
|
def refresh_source_code_and_json_schema(self):
|
81
74
|
"""
|
82
75
|
Refresh name, description, source_code, and json_schema.
|
83
|
-
"""
|
84
|
-
from letta.functions.helpers import generate_model_from_args_json_schema
|
85
|
-
|
86
|
-
if self.tool_type == ToolType.CUSTOM and not self.json_schema:
|
87
|
-
# attempt various fallbacks to get the JSON schema
|
88
|
-
if not self.source_code:
|
89
|
-
logger.error("Custom tool with id=%s is missing source_code field", self.id)
|
90
|
-
raise ValueError(f"Custom tool with id={self.id} is missing source_code field.")
|
91
|
-
|
92
|
-
if self.source_type == ToolSourceType.typescript:
|
93
|
-
# TypeScript tools don't support args_json_schema, only direct schema generation
|
94
|
-
if not self.json_schema:
|
95
|
-
try:
|
96
|
-
from letta.functions.typescript_parser import derive_typescript_json_schema
|
97
76
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
args_schema=args_schema,
|
111
|
-
name=name,
|
112
|
-
description=description,
|
113
|
-
append_heartbeat=False,
|
114
|
-
)
|
115
|
-
else: # elif not self.json_schema: # TODO: JSON schema is not being derived correctly the first time?
|
116
|
-
# If there's not a json_schema provided, then we need to re-derive
|
117
|
-
try:
|
118
|
-
self.json_schema = derive_openai_json_schema(source_code=self.source_code)
|
119
|
-
except Exception as e:
|
120
|
-
logger.error("Failed to derive json schema for tool with id=%s name=%s: %s", self.id, self.name, e)
|
121
|
-
else:
|
122
|
-
raise ValueError(f"Unknown tool source type: {self.source_type}")
|
77
|
+
Note: Schema generation for custom tools is now handled at creation/update time in ToolManager.
|
78
|
+
This method only handles built-in Letta tools.
|
79
|
+
"""
|
80
|
+
if self.tool_type == ToolType.CUSTOM:
|
81
|
+
# Custom tools should already have their schema set during creation/update
|
82
|
+
# No schema generation happens here anymore
|
83
|
+
if not self.json_schema:
|
84
|
+
logger.warning(
|
85
|
+
"Custom tool with id=%s name=%s is missing json_schema. Schema should be set during creation/update.",
|
86
|
+
self.id,
|
87
|
+
self.name,
|
88
|
+
)
|
123
89
|
elif self.tool_type in {ToolType.LETTA_CORE, ToolType.LETTA_MEMORY_CORE, ToolType.LETTA_SLEEPTIME_CORE}:
|
124
90
|
# If it's letta core tool, we generate the json_schema on the fly here
|
125
91
|
self.json_schema = get_json_schema_from_module(module_name=LETTA_CORE_TOOL_MODULE_NAME, function_name=self.name)
|
@@ -135,26 +101,6 @@ class Tool(BaseTool):
|
|
135
101
|
elif self.tool_type in {ToolType.LETTA_FILES_CORE}:
|
136
102
|
# If it's letta files tool, we generate the json_schema on the fly here
|
137
103
|
self.json_schema = get_json_schema_from_module(module_name=LETTA_FILES_TOOL_MODULE_NAME, function_name=self.name)
|
138
|
-
elif self.tool_type in {ToolType.EXTERNAL_COMPOSIO}:
|
139
|
-
# Composio schemas handled separately
|
140
|
-
pass
|
141
|
-
|
142
|
-
# At this point, we need to validate that at least json_schema is populated
|
143
|
-
if not self.json_schema:
|
144
|
-
logger.error("Tool with id=%s name=%s tool_type=%s is missing a json_schema", self.id, self.name, self.tool_type)
|
145
|
-
raise ValueError(f"Tool with id={self.id} name={self.name} tool_type={self.tool_type} is missing a json_schema.")
|
146
|
-
|
147
|
-
# Derive name from the JSON schema if not provided
|
148
|
-
if not self.name:
|
149
|
-
# TODO: This in theory could error, but name should always be on json_schema
|
150
|
-
# TODO: Make JSON schema a typed pydantic object
|
151
|
-
self.name = self.json_schema.get("name")
|
152
|
-
|
153
|
-
# Derive description from the JSON schema if not provided
|
154
|
-
if not self.description:
|
155
|
-
# TODO: This in theory could error, but description should always be on json_schema
|
156
|
-
# TODO: Make JSON schema a typed pydantic object
|
157
|
-
self.description = self.json_schema.get("description")
|
158
104
|
|
159
105
|
return self
|
160
106
|
|
@@ -199,45 +145,6 @@ class ToolCreate(LettaBase):
|
|
199
145
|
json_schema=json_schema,
|
200
146
|
)
|
201
147
|
|
202
|
-
@classmethod
|
203
|
-
def from_composio(cls, action_name: str) -> "ToolCreate":
|
204
|
-
"""
|
205
|
-
Class method to create an instance of Letta-compatible Composio Tool.
|
206
|
-
Check https://docs.composio.dev/introduction/intro/overview to look at options for from_composio
|
207
|
-
|
208
|
-
This function will error if we find more than one tool, or 0 tools.
|
209
|
-
|
210
|
-
Args:
|
211
|
-
action_name str: A action name to filter tools by.
|
212
|
-
Returns:
|
213
|
-
Tool: A Letta Tool initialized with attributes derived from the Composio tool.
|
214
|
-
"""
|
215
|
-
from composio import ComposioToolSet, LogLevel
|
216
|
-
|
217
|
-
composio_toolset = ComposioToolSet(logging_level=LogLevel.ERROR, lock=False)
|
218
|
-
composio_action_schemas = composio_toolset.get_action_schemas(actions=[action_name], check_connected_accounts=False)
|
219
|
-
|
220
|
-
assert len(composio_action_schemas) > 0, "User supplied parameters do not match any Composio tools"
|
221
|
-
assert len(composio_action_schemas) == 1, (
|
222
|
-
f"User supplied parameters match too many Composio tools; {len(composio_action_schemas)} > 1"
|
223
|
-
)
|
224
|
-
|
225
|
-
composio_action_schema = composio_action_schemas[0]
|
226
|
-
|
227
|
-
description = composio_action_schema.description
|
228
|
-
source_type = "python"
|
229
|
-
tags = [COMPOSIO_TOOL_TAG_NAME]
|
230
|
-
wrapper_func_name, wrapper_function_str = generate_composio_tool_wrapper(action_name)
|
231
|
-
json_schema = generate_tool_schema_for_composio(composio_action_schema.parameters, name=wrapper_func_name, description=description)
|
232
|
-
|
233
|
-
return cls(
|
234
|
-
description=description,
|
235
|
-
source_type=source_type,
|
236
|
-
tags=tags,
|
237
|
-
source_code=wrapper_function_str,
|
238
|
-
json_schema=json_schema,
|
239
|
-
)
|
240
|
-
|
241
148
|
|
242
149
|
class ToolUpdate(LettaBase):
|
243
150
|
description: Optional[str] = Field(None, description="The description of the tool.")
|
@@ -253,6 +160,7 @@ class ToolUpdate(LettaBase):
|
|
253
160
|
npm_requirements: list[NpmRequirement] | None = Field(None, description="Optional list of npm packages required by this tool.")
|
254
161
|
metadata_: Optional[Dict[str, Any]] = Field(None, description="A dictionary of additional metadata for the tool.")
|
255
162
|
default_requires_approval: Optional[bool] = Field(None, description="Whether or not to require approval before executing this tool.")
|
163
|
+
# name: Optional[str] = Field(None, description="The name of the tool (must match the JSON schema name and source code function name).")
|
256
164
|
|
257
165
|
model_config = ConfigDict(extra="ignore") # Allows extra fields without validation errors
|
258
166
|
# TODO: Remove this, and clean usage of ToolUpdate everywhere else
|
letta/schemas/tool_rule.py
CHANGED
@@ -2,7 +2,7 @@ import json
|
|
2
2
|
import logging
|
3
3
|
from typing import Annotated, Any, Dict, List, Literal, Optional, Set, Union
|
4
4
|
|
5
|
-
from pydantic import Field, field_validator
|
5
|
+
from pydantic import BaseModel, Field, field_validator, model_validator
|
6
6
|
|
7
7
|
from letta.schemas.enums import ToolRuleType
|
8
8
|
from letta.schemas.letta_base import LettaBase
|
@@ -36,6 +36,30 @@ class BaseToolRule(LettaBase):
|
|
36
36
|
"""Default implementation returns None. Subclasses provide optimized strings."""
|
37
37
|
return None
|
38
38
|
|
39
|
+
@property
|
40
|
+
def requires_force_tool_call(self) -> bool:
|
41
|
+
"""Whether this tool rule requires forcing a tool call in the LLM request when active.
|
42
|
+
When True, the LLM must use a tool; when False, tool use is optional.
|
43
|
+
Default is False for most rules."""
|
44
|
+
return False
|
45
|
+
|
46
|
+
|
47
|
+
class ToolCallNode(BaseModel):
|
48
|
+
"""Typed child override for prefilled arguments.
|
49
|
+
|
50
|
+
When used in a ChildToolRule, if this child is selected next, its `args` will be
|
51
|
+
applied as prefilled arguments (overriding overlapping LLM-provided values).
|
52
|
+
"""
|
53
|
+
|
54
|
+
name: str = Field(..., description="The name of the child tool to invoke next.")
|
55
|
+
args: Optional[Dict[str, Any]] = Field(
|
56
|
+
default=None,
|
57
|
+
description=(
|
58
|
+
"Optional prefilled arguments for this child tool. Keys must match the tool's parameter names and values "
|
59
|
+
"must satisfy the tool's JSON schema. Supports partial prefill; non-overlapping parameters are left to the model."
|
60
|
+
),
|
61
|
+
)
|
62
|
+
|
39
63
|
|
40
64
|
class ChildToolRule(BaseToolRule):
|
41
65
|
"""
|
@@ -43,30 +67,66 @@ class ChildToolRule(BaseToolRule):
|
|
43
67
|
"""
|
44
68
|
|
45
69
|
type: Literal[ToolRuleType.constrain_child_tools] = ToolRuleType.constrain_child_tools
|
70
|
+
|
46
71
|
children: List[str] = Field(..., description="The children tools that can be invoked.")
|
72
|
+
child_arg_nodes: Optional[List[ToolCallNode]] = Field(
|
73
|
+
default=None,
|
74
|
+
description=("Optional list of typed child argument overrides. Each node must reference a child in 'children'."),
|
75
|
+
)
|
47
76
|
prompt_template: Optional[str] = Field(
|
48
77
|
default=None,
|
49
78
|
description="Optional template string (ignored).",
|
50
79
|
)
|
51
80
|
|
81
|
+
@property
|
82
|
+
def requires_force_tool_call(self) -> bool:
|
83
|
+
"""Child tool rules require forcing tool calls."""
|
84
|
+
return True
|
85
|
+
|
52
86
|
def __hash__(self):
|
53
87
|
"""Hash including children list (sorted for consistency)."""
|
54
|
-
|
88
|
+
# Hash on child names only for stability
|
89
|
+
child_names = tuple(sorted(self.children))
|
90
|
+
return hash((self.tool_name, self.type, child_names))
|
55
91
|
|
56
92
|
def __eq__(self, other):
|
57
93
|
"""Equality including children list."""
|
58
94
|
if not isinstance(other, ChildToolRule):
|
59
95
|
return False
|
60
|
-
|
96
|
+
self_names = sorted(self.children)
|
97
|
+
other_names = sorted(other.children)
|
98
|
+
return self.tool_name == other.tool_name and self.type == other.type and self_names == other_names
|
99
|
+
|
100
|
+
def get_child_names(self) -> List[str]:
|
101
|
+
return list(self.children)
|
102
|
+
|
103
|
+
def get_child_args_map(self) -> Dict[str, Dict[str, Any]]:
|
104
|
+
mapping: Dict[str, Dict[str, Any]] = {}
|
105
|
+
if self.child_arg_nodes:
|
106
|
+
for node in self.child_arg_nodes:
|
107
|
+
if node.args:
|
108
|
+
mapping[node.name] = dict(node.args)
|
109
|
+
return mapping
|
61
110
|
|
62
111
|
def get_valid_tools(self, tool_call_history: List[str], available_tools: Set[str], last_function_response: Optional[str]) -> Set[str]:
|
63
112
|
last_tool = tool_call_history[-1] if tool_call_history else None
|
64
|
-
return set(self.
|
113
|
+
return set(self.get_child_names()) if last_tool == self.tool_name else available_tools
|
65
114
|
|
66
115
|
def render_prompt(self) -> str | None:
|
67
|
-
children_str = ", ".join(self.
|
116
|
+
children_str = ", ".join(self.get_child_names())
|
68
117
|
return f"<tool_rule>\nAfter using {self.tool_name}, you must use one of these tools: {children_str}\n</tool_rule>"
|
69
118
|
|
119
|
+
@model_validator(mode="after")
|
120
|
+
def validate_child_arg_nodes(self):
|
121
|
+
if self.child_arg_nodes:
|
122
|
+
child_set = set(self.children)
|
123
|
+
for node in self.child_arg_nodes:
|
124
|
+
if node.name not in child_set:
|
125
|
+
raise ValueError(
|
126
|
+
f"ChildToolRule child_arg_nodes contains a node for '{node.name}' which is not in children {self.children}."
|
127
|
+
)
|
128
|
+
return self
|
129
|
+
|
70
130
|
|
71
131
|
class ParentToolRule(BaseToolRule):
|
72
132
|
"""
|
@@ -77,6 +137,11 @@ class ParentToolRule(BaseToolRule):
|
|
77
137
|
children: List[str] = Field(..., description="The children tools that can be invoked.")
|
78
138
|
prompt_template: Optional[str] = Field(default=None, description="Optional template string (ignored).")
|
79
139
|
|
140
|
+
@property
|
141
|
+
def requires_force_tool_call(self) -> bool:
|
142
|
+
"""Parent tool rules require forcing tool calls."""
|
143
|
+
return True
|
144
|
+
|
80
145
|
def __hash__(self):
|
81
146
|
"""Hash including children list (sorted for consistency)."""
|
82
147
|
return hash((self.tool_name, self.type, tuple(sorted(self.children))))
|
@@ -107,6 +172,11 @@ class ConditionalToolRule(BaseToolRule):
|
|
107
172
|
require_output_mapping: bool = Field(default=False, description="Whether to throw an error when output doesn't match any case")
|
108
173
|
prompt_template: Optional[str] = Field(default=None, description="Optional template string (ignored).")
|
109
174
|
|
175
|
+
@property
|
176
|
+
def requires_force_tool_call(self) -> bool:
|
177
|
+
"""Conditional tool rules require forcing tool calls."""
|
178
|
+
return True
|
179
|
+
|
110
180
|
def __hash__(self):
|
111
181
|
"""Hash including all configuration fields."""
|
112
182
|
# convert dict to sorted tuple of items for consistent hashing
|
@@ -187,6 +257,19 @@ class InitToolRule(BaseToolRule):
|
|
187
257
|
"""
|
188
258
|
|
189
259
|
type: Literal[ToolRuleType.run_first] = ToolRuleType.run_first
|
260
|
+
args: Optional[Dict[str, Any]] = Field(
|
261
|
+
default=None,
|
262
|
+
description=(
|
263
|
+
"Optional prefilled arguments for this tool. When present, these values will override any LLM-provided "
|
264
|
+
"arguments with the same keys during invocation. Keys must match the tool's parameter names and values "
|
265
|
+
"must satisfy the tool's JSON schema. Supports partial prefill; non-overlapping parameters are left to the model."
|
266
|
+
),
|
267
|
+
)
|
268
|
+
|
269
|
+
@property
|
270
|
+
def requires_force_tool_call(self) -> bool:
|
271
|
+
"""Initial tool rules require forcing tool calls."""
|
272
|
+
return True
|
190
273
|
|
191
274
|
|
192
275
|
class TerminalToolRule(BaseToolRule):
|