hammad-python 0.0.19__py3-none-any.whl → 0.0.21__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.
- hammad/__init__.py +7 -137
- hammad/_internal.py +1 -0
- hammad/cli/_runner.py +8 -8
- hammad/cli/plugins.py +55 -26
- hammad/cli/styles/utils.py +16 -8
- hammad/data/__init__.py +1 -5
- hammad/data/collections/__init__.py +2 -3
- hammad/data/collections/collection.py +41 -22
- hammad/data/collections/indexes/__init__.py +1 -1
- hammad/data/collections/indexes/qdrant/__init__.py +1 -1
- hammad/data/collections/indexes/qdrant/index.py +106 -118
- hammad/data/collections/indexes/qdrant/settings.py +14 -14
- hammad/data/collections/indexes/qdrant/utils.py +28 -38
- hammad/data/collections/indexes/tantivy/__init__.py +1 -1
- hammad/data/collections/indexes/tantivy/index.py +57 -59
- hammad/data/collections/indexes/tantivy/settings.py +8 -19
- hammad/data/collections/indexes/tantivy/utils.py +28 -52
- hammad/data/models/__init__.py +2 -7
- hammad/data/sql/__init__.py +1 -1
- hammad/data/sql/database.py +71 -73
- hammad/data/sql/types.py +37 -51
- hammad/formatting/__init__.py +2 -1
- hammad/formatting/json/converters.py +2 -2
- hammad/genai/__init__.py +96 -36
- hammad/genai/agents/__init__.py +47 -1
- hammad/genai/agents/agent.py +1298 -0
- hammad/genai/agents/run.py +615 -0
- hammad/genai/agents/types/__init__.py +29 -22
- hammad/genai/agents/types/agent_context.py +13 -0
- hammad/genai/agents/types/agent_event.py +128 -0
- hammad/genai/agents/types/agent_hooks.py +220 -0
- hammad/genai/agents/types/agent_messages.py +31 -0
- hammad/genai/agents/types/agent_response.py +122 -0
- hammad/genai/agents/types/agent_stream.py +318 -0
- hammad/genai/models/__init__.py +1 -0
- hammad/genai/models/embeddings/__init__.py +39 -0
- hammad/genai/{embedding_models/embedding_model.py → models/embeddings/model.py} +45 -41
- hammad/genai/{embedding_models → models/embeddings}/run.py +10 -8
- hammad/genai/models/embeddings/types/__init__.py +37 -0
- hammad/genai/{embedding_models → models/embeddings/types}/embedding_model_name.py +2 -4
- hammad/genai/{embedding_models → models/embeddings/types}/embedding_model_response.py +11 -4
- hammad/genai/{embedding_models/embedding_model_request.py → models/embeddings/types/embedding_model_run_params.py} +4 -3
- hammad/genai/models/embeddings/types/embedding_model_settings.py +47 -0
- hammad/genai/models/language/__init__.py +48 -0
- hammad/genai/{language_models/language_model.py → models/language/model.py} +496 -204
- hammad/genai/{language_models → models/language}/run.py +80 -57
- hammad/genai/models/language/types/__init__.py +40 -0
- hammad/genai/models/language/types/language_model_instructor_mode.py +47 -0
- hammad/genai/models/language/types/language_model_messages.py +28 -0
- hammad/genai/{language_models/_types.py → models/language/types/language_model_name.py} +3 -40
- hammad/genai/{language_models → models/language/types}/language_model_request.py +17 -25
- hammad/genai/{language_models → models/language/types}/language_model_response.py +60 -67
- hammad/genai/{language_models → models/language/types}/language_model_response_chunk.py +8 -5
- hammad/genai/models/language/types/language_model_settings.py +89 -0
- hammad/genai/{language_models/_streaming.py → models/language/types/language_model_stream.py} +221 -243
- hammad/genai/{language_models/_utils → models/language/utils}/__init__.py +8 -11
- hammad/genai/models/language/utils/requests.py +421 -0
- hammad/genai/{language_models/_utils/_structured_outputs.py → models/language/utils/structured_outputs.py} +31 -20
- hammad/genai/models/model_provider.py +4 -0
- hammad/genai/{multimodal_models.py → models/multimodal.py} +4 -5
- hammad/genai/models/reranking.py +26 -0
- hammad/genai/types/__init__.py +1 -0
- hammad/genai/types/base.py +215 -0
- hammad/genai/{agents/types → types}/history.py +101 -88
- hammad/genai/{agents/types/tool.py → types/tools.py} +157 -140
- hammad/logging/logger.py +9 -1
- hammad/mcp/client/__init__.py +2 -3
- hammad/mcp/client/client.py +10 -10
- hammad/mcp/servers/__init__.py +2 -1
- hammad/service/decorators.py +1 -3
- hammad/web/models.py +1 -3
- hammad/web/search/client.py +10 -22
- {hammad_python-0.0.19.dist-info → hammad_python-0.0.21.dist-info}/METADATA +10 -2
- hammad_python-0.0.21.dist-info/RECORD +127 -0
- hammad/genai/embedding_models/__init__.py +0 -41
- hammad/genai/language_models/__init__.py +0 -35
- hammad/genai/language_models/_utils/_completions.py +0 -131
- hammad/genai/language_models/_utils/_messages.py +0 -89
- hammad/genai/language_models/_utils/_requests.py +0 -202
- hammad/genai/rerank_models.py +0 -26
- hammad_python-0.0.19.dist-info/RECORD +0 -111
- {hammad_python-0.0.19.dist-info → hammad_python-0.0.21.dist-info}/WHEEL +0 -0
- {hammad_python-0.0.19.dist-info → hammad_python-0.0.21.dist-info}/licenses/LICENSE +0 -0
@@ -1,25 +1,38 @@
|
|
1
1
|
"""hammad.genai.language_models.language_model"""
|
2
2
|
|
3
3
|
from typing import (
|
4
|
-
Any,
|
4
|
+
Any,
|
5
5
|
Callable,
|
6
|
-
List,
|
7
|
-
TypeVar,
|
8
|
-
Generic,
|
9
|
-
Union,
|
10
|
-
Optional,
|
11
|
-
Type,
|
12
|
-
overload,
|
13
|
-
Dict,
|
6
|
+
List,
|
7
|
+
TypeVar,
|
8
|
+
Generic,
|
9
|
+
Union,
|
10
|
+
Optional,
|
11
|
+
Type,
|
12
|
+
overload,
|
13
|
+
Dict,
|
14
14
|
TYPE_CHECKING,
|
15
15
|
)
|
16
|
+
import functools
|
17
|
+
import inspect
|
18
|
+
import asyncio
|
16
19
|
from typing_extensions import Literal
|
17
20
|
|
18
21
|
if TYPE_CHECKING:
|
19
22
|
from httpx import Timeout
|
20
23
|
|
21
|
-
from .
|
22
|
-
from
|
24
|
+
from ....logging.logger import _get_internal_logger
|
25
|
+
from ..model_provider import litellm, instructor
|
26
|
+
|
27
|
+
from ...types.base import BaseGenAIModel
|
28
|
+
from .types.language_model_instructor_mode import LanguageModelInstructorMode
|
29
|
+
from .types.language_model_name import LanguageModelName
|
30
|
+
from .types.language_model_messages import LanguageModelMessages
|
31
|
+
from .types.language_model_response import LanguageModelResponse
|
32
|
+
from .types.language_model_settings import LanguageModelSettings
|
33
|
+
from .types.language_model_stream import LanguageModelStream
|
34
|
+
|
35
|
+
from .utils import (
|
23
36
|
parse_messages_input,
|
24
37
|
handle_completion_request_params,
|
25
38
|
handle_completion_response,
|
@@ -29,9 +42,6 @@ from ._utils import (
|
|
29
42
|
format_tool_calls,
|
30
43
|
LanguageModelRequestBuilder,
|
31
44
|
)
|
32
|
-
from .language_model_request import LanguageModelRequest, LanguageModelMessagesParam
|
33
|
-
from .language_model_response import LanguageModelResponse
|
34
|
-
from ._streaming import Stream, AsyncStream
|
35
45
|
|
36
46
|
__all__ = [
|
37
47
|
"LanguageModel",
|
@@ -41,9 +51,12 @@ __all__ = [
|
|
41
51
|
T = TypeVar("T")
|
42
52
|
|
43
53
|
|
54
|
+
logger = _get_internal_logger(__name__)
|
55
|
+
|
56
|
+
|
44
57
|
class LanguageModelError(Exception):
|
45
58
|
"""Error raised when an error occurs during a language model operation."""
|
46
|
-
|
59
|
+
|
47
60
|
def __init__(self, message: str, *args: Any, **kwargs: Any):
|
48
61
|
super().__init__(message, *args, **kwargs)
|
49
62
|
self.message = message
|
@@ -51,117 +64,111 @@ class LanguageModelError(Exception):
|
|
51
64
|
self.kwargs = kwargs
|
52
65
|
|
53
66
|
|
54
|
-
class
|
55
|
-
"""
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
if _AIProvider._INSTRUCTOR is None:
|
84
|
-
try:
|
85
|
-
import instructor
|
86
|
-
_AIProvider._INSTRUCTOR = instructor
|
87
|
-
except ImportError as e:
|
88
|
-
raise ImportError(
|
89
|
-
"Using the `hammad.ai.llms` extension requires the `instructor` package to be installed.\n"
|
90
|
-
"Please either install the `instructor` package, or install the `hammad.ai` extension with:\n"
|
91
|
-
"`pip install 'hammad-python[ai]'`"
|
92
|
-
) from e
|
93
|
-
return _AIProvider._INSTRUCTOR
|
94
|
-
|
95
|
-
|
96
|
-
class LanguageModel(Generic[T]):
|
97
|
-
"""A clean language model interface for generating responses with comprehensive
|
98
|
-
parameter handling and type safety."""
|
99
|
-
|
67
|
+
class LanguageModel(BaseGenAIModel, Generic[T]):
|
68
|
+
"""
|
69
|
+
A Generative AI model that can be used to generate text, chat completions,
|
70
|
+
structured outputs, call tools and more.
|
71
|
+
"""
|
72
|
+
|
73
|
+
model: LanguageModelName | str = "openai/gpt-4o-mini"
|
74
|
+
"""The model to use for requests."""
|
75
|
+
|
76
|
+
type: Type[T] = str
|
77
|
+
"""The type of the output of the language model."""
|
78
|
+
|
79
|
+
instructions: Optional[str] = None
|
80
|
+
"""Instructions for the language model."""
|
81
|
+
|
82
|
+
base_url: Optional[str] = None
|
83
|
+
api_key: Optional[str] = None
|
84
|
+
api_version: Optional[str] = None
|
85
|
+
organization: Optional[str] = None
|
86
|
+
deployment_id: Optional[str] = None
|
87
|
+
model_list: Optional[List[Any]] = None
|
88
|
+
extra_headers: Optional[Dict[str, str]] = None
|
89
|
+
|
90
|
+
settings: LanguageModelSettings = LanguageModelSettings()
|
91
|
+
"""Settings for the language model."""
|
92
|
+
|
93
|
+
instructor_mode: LanguageModelInstructorMode = "tool_call"
|
94
|
+
"""Default instructor mode for structured outputs."""
|
95
|
+
|
100
96
|
def __init__(
|
101
97
|
self,
|
102
98
|
model: LanguageModelName = "openai/gpt-4o-mini",
|
103
99
|
base_url: Optional[str] = None,
|
104
100
|
api_key: Optional[str] = None,
|
105
101
|
instructor_mode: LanguageModelInstructorMode = "tool_call",
|
102
|
+
**kwargs: Any,
|
106
103
|
):
|
107
104
|
"""Initialize the language model.
|
108
|
-
|
105
|
+
|
109
106
|
Args:
|
110
107
|
model: The model to use for requests
|
108
|
+
base_url: Custom base URL for the API
|
109
|
+
api_key: API key for authentication
|
111
110
|
instructor_mode: Default instructor mode for structured outputs
|
111
|
+
**kwargs: Additional arguments passed to BaseGenAIModel
|
112
112
|
"""
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
113
|
+
# Initialize BaseGenAIModel via super()
|
114
|
+
super().__init__(model=model, base_url=base_url, api_key=api_key, **kwargs)
|
115
|
+
|
116
|
+
# Initialize LanguageModel-specific attributes
|
117
117
|
self._instructor_client = None
|
118
|
-
|
119
|
-
|
118
|
+
|
119
|
+
logger.info(f"Initialized LanguageModel w/ model: {self.model}")
|
120
|
+
logger.debug(f"LanguageModel settings: {self.settings}")
|
121
|
+
|
122
|
+
def _get_instructor_client(
|
123
|
+
self, mode: Optional[LanguageModelInstructorMode] = None
|
124
|
+
):
|
120
125
|
"""Get or create an instructor client with the specified mode."""
|
121
126
|
effective_mode = mode or self.instructor_mode
|
122
|
-
|
127
|
+
|
123
128
|
# Create a new client if mode changed or client doesn't exist
|
124
|
-
if (
|
125
|
-
|
126
|
-
|
127
|
-
|
129
|
+
if (
|
130
|
+
self._instructor_client is None
|
131
|
+
or getattr(self._instructor_client, "_mode", None) != effective_mode
|
132
|
+
):
|
133
|
+
logger.debug(f"Creating new instructor client for mode: {effective_mode} from old mode: {getattr(self._instructor_client, '_mode', None)}")
|
134
|
+
|
128
135
|
self._instructor_client = instructor.from_litellm(
|
129
|
-
completion=
|
130
|
-
mode=instructor.Mode(effective_mode)
|
136
|
+
completion=litellm.completion, mode=instructor.Mode(effective_mode)
|
131
137
|
)
|
132
138
|
self._instructor_client._mode = effective_mode
|
133
|
-
|
139
|
+
|
134
140
|
return self._instructor_client
|
135
|
-
|
136
|
-
def _get_async_instructor_client(
|
141
|
+
|
142
|
+
def _get_async_instructor_client(
|
143
|
+
self, mode: Optional[LanguageModelInstructorMode] = None
|
144
|
+
):
|
137
145
|
"""Get or create an async instructor client with the specified mode."""
|
138
146
|
effective_mode = mode or self.instructor_mode
|
139
|
-
|
140
|
-
instructor = _AIProvider.get_instructor()
|
147
|
+
|
141
148
|
return instructor.from_litellm(
|
142
|
-
completion=
|
143
|
-
mode=instructor.Mode(effective_mode)
|
149
|
+
completion=litellm.acompletion, mode=instructor.Mode(effective_mode)
|
144
150
|
)
|
145
|
-
|
151
|
+
|
146
152
|
# Overloaded run methods for different return types
|
147
|
-
|
153
|
+
|
148
154
|
@overload
|
149
155
|
def run(
|
150
156
|
self,
|
151
|
-
messages:
|
157
|
+
messages: LanguageModelMessages,
|
152
158
|
instructions: Optional[str] = None,
|
153
159
|
*,
|
154
160
|
stream: Literal[False] = False,
|
155
161
|
model: Optional[LanguageModelName | str] = None,
|
156
162
|
base_url: Optional[str] = None,
|
157
163
|
api_key: Optional[str] = None,
|
164
|
+
mock_response: Optional[str] = None,
|
158
165
|
**kwargs: Any,
|
159
166
|
) -> LanguageModelResponse[str]: ...
|
160
|
-
|
167
|
+
|
161
168
|
@overload
|
162
169
|
def run(
|
163
170
|
self,
|
164
|
-
messages:
|
171
|
+
messages: LanguageModelMessages,
|
165
172
|
instructions: Optional[str] = None,
|
166
173
|
*,
|
167
174
|
stream: Literal[False] = False,
|
@@ -178,26 +185,28 @@ class LanguageModel(Generic[T]):
|
|
178
185
|
frequency_penalty: Optional[float] = None,
|
179
186
|
seed: Optional[int] = None,
|
180
187
|
user: Optional[str] = None,
|
188
|
+
mock_response: Optional[str] = None,
|
181
189
|
**kwargs: Any,
|
182
190
|
) -> LanguageModelResponse[str]: ...
|
183
|
-
|
191
|
+
|
184
192
|
@overload
|
185
193
|
def run(
|
186
194
|
self,
|
187
|
-
messages:
|
195
|
+
messages: LanguageModelMessages,
|
188
196
|
instructions: Optional[str] = None,
|
189
197
|
*,
|
190
198
|
stream: Literal[True],
|
191
199
|
model: Optional[LanguageModelName | str] = None,
|
192
200
|
base_url: Optional[str] = None,
|
193
201
|
api_key: Optional[str] = None,
|
202
|
+
mock_response: Optional[str] = None,
|
194
203
|
**kwargs: Any,
|
195
|
-
) ->
|
196
|
-
|
204
|
+
) -> LanguageModelStream[str]: ...
|
205
|
+
|
197
206
|
@overload
|
198
207
|
def run(
|
199
208
|
self,
|
200
|
-
messages:
|
209
|
+
messages: LanguageModelMessages,
|
201
210
|
instructions: Optional[str] = None,
|
202
211
|
*,
|
203
212
|
stream: Literal[True],
|
@@ -214,13 +223,14 @@ class LanguageModel(Generic[T]):
|
|
214
223
|
frequency_penalty: Optional[float] = None,
|
215
224
|
seed: Optional[int] = None,
|
216
225
|
user: Optional[str] = None,
|
226
|
+
mock_response: Optional[str] = None,
|
217
227
|
**kwargs: Any,
|
218
|
-
) ->
|
219
|
-
|
228
|
+
) -> LanguageModelStream[str]: ...
|
229
|
+
|
220
230
|
@overload
|
221
231
|
def run(
|
222
232
|
self,
|
223
|
-
messages:
|
233
|
+
messages: LanguageModelMessages,
|
224
234
|
instructions: Optional[str] = None,
|
225
235
|
*,
|
226
236
|
type: Type[T],
|
@@ -228,13 +238,14 @@ class LanguageModel(Generic[T]):
|
|
228
238
|
model: Optional[LanguageModelName | str] = None,
|
229
239
|
base_url: Optional[str] = None,
|
230
240
|
api_key: Optional[str] = None,
|
241
|
+
mock_response: Optional[str] = None,
|
231
242
|
**kwargs: Any,
|
232
243
|
) -> LanguageModelResponse[T]: ...
|
233
|
-
|
244
|
+
|
234
245
|
@overload
|
235
246
|
def run(
|
236
247
|
self,
|
237
|
-
messages:
|
248
|
+
messages: LanguageModelMessages,
|
238
249
|
instructions: Optional[str] = None,
|
239
250
|
*,
|
240
251
|
type: Type[T],
|
@@ -263,13 +274,14 @@ class LanguageModel(Generic[T]):
|
|
263
274
|
frequency_penalty: Optional[float] = None,
|
264
275
|
seed: Optional[int] = None,
|
265
276
|
user: Optional[str] = None,
|
277
|
+
mock_response: Optional[str] = None,
|
266
278
|
**kwargs: Any,
|
267
279
|
) -> LanguageModelResponse[T]: ...
|
268
|
-
|
280
|
+
|
269
281
|
@overload
|
270
282
|
def run(
|
271
283
|
self,
|
272
|
-
messages:
|
284
|
+
messages: LanguageModelMessages,
|
273
285
|
instructions: Optional[str] = None,
|
274
286
|
*,
|
275
287
|
type: Type[T],
|
@@ -277,13 +289,14 @@ class LanguageModel(Generic[T]):
|
|
277
289
|
model: Optional[LanguageModelName | str] = None,
|
278
290
|
base_url: Optional[str] = None,
|
279
291
|
api_key: Optional[str] = None,
|
292
|
+
mock_response: Optional[str] = None,
|
280
293
|
**kwargs: Any,
|
281
|
-
) ->
|
282
|
-
|
294
|
+
) -> LanguageModelStream[T]: ...
|
295
|
+
|
283
296
|
@overload
|
284
297
|
def run(
|
285
298
|
self,
|
286
|
-
messages:
|
299
|
+
messages: LanguageModelMessages,
|
287
300
|
instructions: Optional[str] = None,
|
288
301
|
*,
|
289
302
|
type: Type[T],
|
@@ -312,77 +325,87 @@ class LanguageModel(Generic[T]):
|
|
312
325
|
frequency_penalty: Optional[float] = None,
|
313
326
|
seed: Optional[int] = None,
|
314
327
|
user: Optional[str] = None,
|
328
|
+
mock_response: Optional[str] = None,
|
315
329
|
**kwargs: Any,
|
316
|
-
) ->
|
317
|
-
|
330
|
+
) -> LanguageModelStream[T]: ...
|
331
|
+
|
318
332
|
def run(
|
319
333
|
self,
|
320
|
-
messages:
|
334
|
+
messages: LanguageModelMessages,
|
321
335
|
instructions: Optional[str] = None,
|
336
|
+
mock_response: Optional[str] = None,
|
322
337
|
**kwargs: Any,
|
323
|
-
) -> Union[LanguageModelResponse[Any],
|
338
|
+
) -> Union[LanguageModelResponse[Any], LanguageModelStream[Any]]:
|
324
339
|
"""Run a language model request.
|
325
|
-
|
340
|
+
|
326
341
|
Args:
|
327
342
|
messages: The input messages/content for the request
|
328
343
|
instructions: Optional system instructions to prepend
|
344
|
+
mock_response: Mock response string for testing (saves API costs)
|
329
345
|
**kwargs: Additional request parameters
|
330
|
-
|
346
|
+
|
331
347
|
Returns:
|
332
348
|
LanguageModelResponse or LanguageModelStream depending on parameters
|
333
349
|
"""
|
350
|
+
logger.info(f"Running LanguageModel request with model: {self.model}")
|
351
|
+
logger.debug(f"LanguageModel request kwargs: {kwargs}")
|
352
|
+
|
334
353
|
try:
|
335
|
-
# Extract model, base_url, and
|
354
|
+
# Extract model, base_url, api_key, and mock_response from kwargs, using instance defaults
|
336
355
|
model = kwargs.pop("model", None) or self.model
|
337
356
|
base_url = kwargs.pop("base_url", None) or self.base_url
|
338
357
|
api_key = kwargs.pop("api_key", None) or self.api_key
|
339
|
-
|
340
|
-
|
358
|
+
mock_response_param = kwargs.pop("mock_response", None) or mock_response
|
359
|
+
|
360
|
+
# Add base_url, api_key, and mock_response to kwargs if they are set
|
341
361
|
if base_url is not None:
|
342
362
|
kwargs["base_url"] = base_url
|
343
363
|
if api_key is not None:
|
344
364
|
kwargs["api_key"] = api_key
|
345
|
-
|
365
|
+
if mock_response_param is not None:
|
366
|
+
kwargs["mock_response"] = mock_response_param
|
367
|
+
|
346
368
|
# Create the request
|
347
369
|
request = LanguageModelRequestBuilder(
|
348
|
-
messages=messages,
|
349
|
-
instructions=instructions,
|
350
|
-
model=model,
|
351
|
-
**kwargs
|
370
|
+
messages=messages, instructions=instructions, model=model, **kwargs
|
352
371
|
)
|
353
|
-
|
372
|
+
|
354
373
|
# Parse messages
|
355
|
-
parsed_messages = parse_messages_input(
|
356
|
-
|
357
|
-
|
374
|
+
parsed_messages = parse_messages_input(
|
375
|
+
request.messages, request.instructions
|
376
|
+
)
|
377
|
+
if request.is_structured_output():
|
378
|
+
parsed_messages = format_tool_calls(parsed_messages)
|
379
|
+
|
358
380
|
# Handle different request types
|
359
381
|
if request.is_structured_output():
|
360
382
|
return self._handle_structured_output_request(request, parsed_messages)
|
361
383
|
else:
|
362
384
|
return self._handle_completion_request(request, parsed_messages)
|
363
|
-
|
385
|
+
|
364
386
|
except Exception as e:
|
365
387
|
raise LanguageModelError(f"Error in language model request: {e}") from e
|
366
|
-
|
388
|
+
|
367
389
|
# Overloaded async_run methods for different return types
|
368
|
-
|
390
|
+
|
369
391
|
@overload
|
370
392
|
async def async_run(
|
371
393
|
self,
|
372
|
-
messages:
|
394
|
+
messages: LanguageModelMessages,
|
373
395
|
instructions: Optional[str] = None,
|
374
396
|
*,
|
375
397
|
stream: Literal[False] = False,
|
376
398
|
model: Optional[LanguageModelName | str] = None,
|
377
399
|
base_url: Optional[str] = None,
|
378
400
|
api_key: Optional[str] = None,
|
401
|
+
mock_response: Optional[str] = None,
|
379
402
|
**kwargs: Any,
|
380
403
|
) -> LanguageModelResponse[str]: ...
|
381
|
-
|
404
|
+
|
382
405
|
@overload
|
383
406
|
async def async_run(
|
384
407
|
self,
|
385
|
-
messages:
|
408
|
+
messages: LanguageModelMessages,
|
386
409
|
instructions: Optional[str] = None,
|
387
410
|
*,
|
388
411
|
stream: Literal[False] = False,
|
@@ -399,26 +422,28 @@ class LanguageModel(Generic[T]):
|
|
399
422
|
frequency_penalty: Optional[float] = None,
|
400
423
|
seed: Optional[int] = None,
|
401
424
|
user: Optional[str] = None,
|
425
|
+
mock_response: Optional[str] = None,
|
402
426
|
**kwargs: Any,
|
403
427
|
) -> LanguageModelResponse[str]: ...
|
404
|
-
|
428
|
+
|
405
429
|
@overload
|
406
430
|
async def async_run(
|
407
431
|
self,
|
408
|
-
messages:
|
432
|
+
messages: LanguageModelMessages,
|
409
433
|
instructions: Optional[str] = None,
|
410
434
|
*,
|
411
435
|
stream: Literal[True],
|
412
436
|
model: Optional[LanguageModelName | str] = None,
|
413
437
|
base_url: Optional[str] = None,
|
414
438
|
api_key: Optional[str] = None,
|
439
|
+
mock_response: Optional[str] = None,
|
415
440
|
**kwargs: Any,
|
416
|
-
) ->
|
417
|
-
|
441
|
+
) -> LanguageModelStream[str]: ...
|
442
|
+
|
418
443
|
@overload
|
419
444
|
async def async_run(
|
420
445
|
self,
|
421
|
-
messages:
|
446
|
+
messages: LanguageModelMessages,
|
422
447
|
instructions: Optional[str] = None,
|
423
448
|
*,
|
424
449
|
stream: Literal[True],
|
@@ -435,13 +460,14 @@ class LanguageModel(Generic[T]):
|
|
435
460
|
frequency_penalty: Optional[float] = None,
|
436
461
|
seed: Optional[int] = None,
|
437
462
|
user: Optional[str] = None,
|
463
|
+
mock_response: Optional[str] = None,
|
438
464
|
**kwargs: Any,
|
439
|
-
) ->
|
440
|
-
|
465
|
+
) -> LanguageModelStream[str]: ...
|
466
|
+
|
441
467
|
@overload
|
442
468
|
async def async_run(
|
443
469
|
self,
|
444
|
-
messages:
|
470
|
+
messages: LanguageModelMessages,
|
445
471
|
instructions: Optional[str] = None,
|
446
472
|
*,
|
447
473
|
type: Type[T],
|
@@ -449,13 +475,14 @@ class LanguageModel(Generic[T]):
|
|
449
475
|
model: Optional[LanguageModelName | str] = None,
|
450
476
|
base_url: Optional[str] = None,
|
451
477
|
api_key: Optional[str] = None,
|
478
|
+
mock_response: Optional[str] = None,
|
452
479
|
**kwargs: Any,
|
453
480
|
) -> LanguageModelResponse[T]: ...
|
454
|
-
|
481
|
+
|
455
482
|
@overload
|
456
483
|
async def async_run(
|
457
484
|
self,
|
458
|
-
messages:
|
485
|
+
messages: LanguageModelMessages,
|
459
486
|
instructions: Optional[str] = None,
|
460
487
|
*,
|
461
488
|
type: Type[T],
|
@@ -484,13 +511,14 @@ class LanguageModel(Generic[T]):
|
|
484
511
|
frequency_penalty: Optional[float] = None,
|
485
512
|
seed: Optional[int] = None,
|
486
513
|
user: Optional[str] = None,
|
514
|
+
mock_response: Optional[str] = None,
|
487
515
|
**kwargs: Any,
|
488
516
|
) -> LanguageModelResponse[T]: ...
|
489
|
-
|
517
|
+
|
490
518
|
@overload
|
491
519
|
async def async_run(
|
492
520
|
self,
|
493
|
-
messages:
|
521
|
+
messages: LanguageModelMessages,
|
494
522
|
instructions: Optional[str] = None,
|
495
523
|
*,
|
496
524
|
type: Type[T],
|
@@ -498,13 +526,14 @@ class LanguageModel(Generic[T]):
|
|
498
526
|
model: Optional[LanguageModelName | str] = None,
|
499
527
|
base_url: Optional[str] = None,
|
500
528
|
api_key: Optional[str] = None,
|
529
|
+
mock_response: Optional[str] = None,
|
501
530
|
**kwargs: Any,
|
502
|
-
) ->
|
503
|
-
|
531
|
+
) -> LanguageModelStream[T]: ...
|
532
|
+
|
504
533
|
@overload
|
505
534
|
async def async_run(
|
506
535
|
self,
|
507
|
-
messages:
|
536
|
+
messages: LanguageModelMessages,
|
508
537
|
instructions: Optional[str] = None,
|
509
538
|
*,
|
510
539
|
type: Type[T],
|
@@ -533,114 +562,129 @@ class LanguageModel(Generic[T]):
|
|
533
562
|
frequency_penalty: Optional[float] = None,
|
534
563
|
seed: Optional[int] = None,
|
535
564
|
user: Optional[str] = None,
|
565
|
+
mock_response: Optional[str] = None,
|
536
566
|
**kwargs: Any,
|
537
|
-
) ->
|
538
|
-
|
567
|
+
) -> LanguageModelStream[T]: ...
|
568
|
+
|
539
569
|
async def async_run(
|
540
570
|
self,
|
541
|
-
messages:
|
571
|
+
messages: LanguageModelMessages,
|
542
572
|
instructions: Optional[str] = None,
|
573
|
+
mock_response: Optional[str] = None,
|
543
574
|
**kwargs: Any,
|
544
|
-
) -> Union[LanguageModelResponse[Any],
|
575
|
+
) -> Union[LanguageModelResponse[Any], LanguageModelStream[Any]]:
|
545
576
|
"""Run an async language model request.
|
546
|
-
|
577
|
+
|
547
578
|
Args:
|
548
579
|
messages: The input messages/content for the request
|
549
580
|
instructions: Optional system instructions to prepend
|
581
|
+
mock_response: Mock response string for testing (saves API costs)
|
550
582
|
**kwargs: Additional request parameters
|
551
|
-
|
583
|
+
|
552
584
|
Returns:
|
553
585
|
LanguageModelResponse or LanguageModelAsyncStream depending on parameters
|
554
586
|
"""
|
587
|
+
logger.info(f"Running async LanguageModel request with model: {self.model}")
|
588
|
+
logger.debug(f"LanguageModel request kwargs: {kwargs}")
|
589
|
+
|
555
590
|
try:
|
556
|
-
# Extract model, base_url, and
|
591
|
+
# Extract model, base_url, api_key, and mock_response from kwargs, using instance defaults
|
557
592
|
model = kwargs.pop("model", None) or self.model
|
558
593
|
base_url = kwargs.pop("base_url", None) or self.base_url
|
559
594
|
api_key = kwargs.pop("api_key", None) or self.api_key
|
560
|
-
|
561
|
-
|
595
|
+
mock_response_param = kwargs.pop("mock_response", None) or mock_response
|
596
|
+
|
597
|
+
# Add base_url, api_key, and mock_response to kwargs if they are set
|
562
598
|
if base_url is not None:
|
563
599
|
kwargs["base_url"] = base_url
|
564
600
|
if api_key is not None:
|
565
601
|
kwargs["api_key"] = api_key
|
566
|
-
|
602
|
+
if mock_response_param is not None:
|
603
|
+
kwargs["mock_response"] = mock_response_param
|
604
|
+
|
567
605
|
# Create the request
|
568
606
|
request = LanguageModelRequestBuilder(
|
569
|
-
messages=messages,
|
570
|
-
instructions=instructions,
|
571
|
-
model=model,
|
572
|
-
**kwargs
|
607
|
+
messages=messages, instructions=instructions, model=model, **kwargs
|
573
608
|
)
|
574
|
-
|
609
|
+
|
575
610
|
# Parse messages
|
576
|
-
parsed_messages = parse_messages_input(
|
577
|
-
|
578
|
-
|
611
|
+
parsed_messages = parse_messages_input(
|
612
|
+
request.messages, request.instructions
|
613
|
+
)
|
614
|
+
if request.is_structured_output():
|
615
|
+
parsed_messages = format_tool_calls(parsed_messages)
|
616
|
+
|
579
617
|
# Handle different request types
|
580
618
|
if request.is_structured_output():
|
581
|
-
return await self._handle_async_structured_output_request(
|
619
|
+
return await self._handle_async_structured_output_request(
|
620
|
+
request, parsed_messages
|
621
|
+
)
|
582
622
|
else:
|
583
|
-
return await self._handle_async_completion_request(
|
584
|
-
|
623
|
+
return await self._handle_async_completion_request(
|
624
|
+
request, parsed_messages
|
625
|
+
)
|
626
|
+
|
585
627
|
except Exception as e:
|
586
|
-
raise LanguageModelError(
|
587
|
-
|
628
|
+
raise LanguageModelError(
|
629
|
+
f"Error in async language model request: {e}"
|
630
|
+
) from e
|
631
|
+
|
588
632
|
def _handle_completion_request(
|
589
|
-
self,
|
590
|
-
|
591
|
-
parsed_messages: List[Any]
|
592
|
-
) -> Union[LanguageModelResponse[str], Stream[str]]:
|
633
|
+
self, request: LanguageModelRequestBuilder, parsed_messages: List[Any]
|
634
|
+
) -> Union[LanguageModelResponse[str], LanguageModelStream[str]]:
|
593
635
|
"""Handle a standard completion request."""
|
594
636
|
# Get filtered parameters
|
595
637
|
params = handle_completion_request_params(request.get_completion_settings())
|
596
638
|
params["messages"] = parsed_messages
|
597
|
-
|
598
|
-
litellm = _AIProvider.get_litellm()
|
599
|
-
|
639
|
+
|
600
640
|
if request.is_streaming():
|
601
641
|
# Handle streaming - stream parameter is already in params
|
602
642
|
if "stream_options" not in params and "stream_options" in request.settings:
|
603
643
|
params["stream_options"] = request.settings["stream_options"]
|
604
644
|
stream = litellm.completion(**params)
|
605
|
-
return
|
645
|
+
return LanguageModelStream(
|
646
|
+
model=request.model,
|
647
|
+
stream=stream,
|
648
|
+
output_type=str,
|
649
|
+
)
|
606
650
|
else:
|
607
651
|
# Handle non-streaming
|
608
652
|
response = litellm.completion(**params)
|
609
653
|
return handle_completion_response(response, request.model)
|
610
|
-
|
654
|
+
|
611
655
|
async def _handle_async_completion_request(
|
612
|
-
self,
|
613
|
-
|
614
|
-
parsed_messages: List[Any]
|
615
|
-
) -> Union[LanguageModelResponse[str], AsyncStream[str]]:
|
656
|
+
self, request: LanguageModelRequestBuilder, parsed_messages: List[Any]
|
657
|
+
) -> Union[LanguageModelResponse[str], LanguageModelStream[str]]:
|
616
658
|
"""Handle an async standard completion request."""
|
617
659
|
# Get filtered parameters
|
618
660
|
params = handle_completion_request_params(request.get_completion_settings())
|
619
661
|
params["messages"] = parsed_messages
|
620
|
-
|
621
|
-
litellm = _AIProvider.get_litellm()
|
622
|
-
|
662
|
+
|
623
663
|
if request.is_streaming():
|
624
664
|
# Handle streaming - stream parameter is already in params
|
625
665
|
if "stream_options" not in params and "stream_options" in request.settings:
|
626
666
|
params["stream_options"] = request.settings["stream_options"]
|
627
667
|
stream = await litellm.acompletion(**params)
|
628
|
-
return
|
668
|
+
return LanguageModelStream(
|
669
|
+
model=request.model,
|
670
|
+
stream=stream,
|
671
|
+
output_type=str,
|
672
|
+
)
|
629
673
|
else:
|
630
674
|
# Handle non-streaming
|
631
675
|
response = await litellm.acompletion(**params)
|
632
676
|
return handle_completion_response(response, request.model)
|
633
|
-
|
677
|
+
|
634
678
|
def _handle_structured_output_request(
|
635
|
-
self,
|
636
|
-
|
637
|
-
parsed_messages: List[Any]
|
638
|
-
) -> Union[LanguageModelResponse[Any], Stream[Any]]:
|
679
|
+
self, request: LanguageModelRequestBuilder, parsed_messages: List[Any]
|
680
|
+
) -> Union[LanguageModelResponse[Any], LanguageModelStream[Any]]:
|
639
681
|
"""Handle a structured output request."""
|
640
682
|
# Get filtered parameters
|
641
|
-
params = handle_structured_output_request_params(
|
683
|
+
params = handle_structured_output_request_params(
|
684
|
+
request.get_structured_output_settings()
|
685
|
+
)
|
642
686
|
params["messages"] = parsed_messages
|
643
|
-
|
687
|
+
|
644
688
|
# Prepare response model
|
645
689
|
response_model = prepare_response_model(
|
646
690
|
request.get_output_type(),
|
@@ -648,10 +692,10 @@ class LanguageModel(Generic[T]):
|
|
648
692
|
request.get_response_field_instruction(),
|
649
693
|
request.get_response_model_name(),
|
650
694
|
)
|
651
|
-
|
695
|
+
|
652
696
|
# Get instructor client
|
653
697
|
client = self._get_instructor_client(request.get_instructor_mode())
|
654
|
-
|
698
|
+
|
655
699
|
if request.is_streaming():
|
656
700
|
if isinstance(request.get_output_type(), list):
|
657
701
|
# Handle streaming - stream parameter is already in params
|
@@ -669,7 +713,12 @@ class LanguageModel(Generic[T]):
|
|
669
713
|
strict=request.get_strict_mode(),
|
670
714
|
**params,
|
671
715
|
)
|
672
|
-
return
|
716
|
+
return LanguageModelStream(
|
717
|
+
model=request.model,
|
718
|
+
stream=stream,
|
719
|
+
output_type=request.get_output_type(),
|
720
|
+
response_field_name=request.get_response_field_name(),
|
721
|
+
)
|
673
722
|
else:
|
674
723
|
# Handle non-streaming
|
675
724
|
response, completion = client.chat.completions.create_with_completion(
|
@@ -679,19 +728,23 @@ class LanguageModel(Generic[T]):
|
|
679
728
|
**params,
|
680
729
|
)
|
681
730
|
return handle_structured_output_response(
|
682
|
-
response,
|
731
|
+
response,
|
732
|
+
completion,
|
733
|
+
request.model,
|
734
|
+
request.get_output_type(),
|
735
|
+
request.get_response_field_name(),
|
683
736
|
)
|
684
|
-
|
737
|
+
|
685
738
|
async def _handle_async_structured_output_request(
|
686
|
-
self,
|
687
|
-
|
688
|
-
parsed_messages: List[Any]
|
689
|
-
) -> Union[LanguageModelResponse[Any], AsyncStream[Any]]:
|
739
|
+
self, request: LanguageModelRequestBuilder, parsed_messages: List[Any]
|
740
|
+
) -> Union[LanguageModelResponse[Any], LanguageModelStream[Any]]:
|
690
741
|
"""Handle an async structured output request."""
|
691
742
|
# Get filtered parameters
|
692
|
-
params = handle_structured_output_request_params(
|
743
|
+
params = handle_structured_output_request_params(
|
744
|
+
request.get_structured_output_settings()
|
745
|
+
)
|
693
746
|
params["messages"] = parsed_messages
|
694
|
-
|
747
|
+
|
695
748
|
# Prepare response model
|
696
749
|
response_model = prepare_response_model(
|
697
750
|
request.get_output_type(),
|
@@ -699,10 +752,10 @@ class LanguageModel(Generic[T]):
|
|
699
752
|
request.get_response_field_instruction(),
|
700
753
|
request.get_response_model_name(),
|
701
754
|
)
|
702
|
-
|
755
|
+
|
703
756
|
# Get async instructor client
|
704
757
|
client = self._get_async_instructor_client(request.get_instructor_mode())
|
705
|
-
|
758
|
+
|
706
759
|
if request.is_streaming():
|
707
760
|
if isinstance(request.get_output_type(), list):
|
708
761
|
# Handle streaming - stream parameter is already in params
|
@@ -720,7 +773,12 @@ class LanguageModel(Generic[T]):
|
|
720
773
|
strict=request.get_strict_mode(),
|
721
774
|
**params,
|
722
775
|
)
|
723
|
-
return
|
776
|
+
return LanguageModelStream(
|
777
|
+
model=request.model,
|
778
|
+
stream=stream,
|
779
|
+
output_type=request.get_output_type(),
|
780
|
+
response_field_name=request.get_response_field_name(),
|
781
|
+
)
|
724
782
|
else:
|
725
783
|
# Handle non-streaming
|
726
784
|
response, completion = await client.chat.completions.create_with_completion(
|
@@ -730,5 +788,239 @@ class LanguageModel(Generic[T]):
|
|
730
788
|
**params,
|
731
789
|
)
|
732
790
|
return handle_structured_output_response(
|
733
|
-
response,
|
734
|
-
|
791
|
+
response,
|
792
|
+
completion,
|
793
|
+
request.model,
|
794
|
+
request.get_output_type(),
|
795
|
+
request.get_response_field_name(),
|
796
|
+
)
|
797
|
+
|
798
|
+
def as_tool(
|
799
|
+
self,
|
800
|
+
func: Optional[Callable] = None,
|
801
|
+
*,
|
802
|
+
name: Optional[str] = None,
|
803
|
+
description: Optional[str] = None,
|
804
|
+
instructions: Optional[str] = None,
|
805
|
+
**kwargs: Any,
|
806
|
+
) -> Union[Callable, Any]:
|
807
|
+
"""Convert this language model to a tool that can be used by agents.
|
808
|
+
|
809
|
+
Can be used as a decorator or as a function:
|
810
|
+
|
811
|
+
As a decorator:
|
812
|
+
@model.as_tool()
|
813
|
+
def my_function(param1: str, param2: int) -> MyType:
|
814
|
+
'''Function description'''
|
815
|
+
pass
|
816
|
+
|
817
|
+
As a function:
|
818
|
+
tool = model.as_tool(
|
819
|
+
name="my_tool",
|
820
|
+
description="Tool description",
|
821
|
+
instructions="Custom instructions for the LLM"
|
822
|
+
)
|
823
|
+
|
824
|
+
Args:
|
825
|
+
func: The function to wrap (when used as decorator)
|
826
|
+
name: The name of the tool
|
827
|
+
description: Description of what the tool does
|
828
|
+
instructions: Custom instructions for the LLM generation
|
829
|
+
**kwargs: Additional arguments for tool creation
|
830
|
+
|
831
|
+
Returns:
|
832
|
+
BaseTool or decorated function
|
833
|
+
"""
|
834
|
+
from ...types.base import BaseTool
|
835
|
+
from ....formatting.text.converters import convert_docstring_to_text
|
836
|
+
|
837
|
+
def create_tool_wrapper(target_func: Optional[Callable] = None) -> Any:
|
838
|
+
"""Create a tool wrapper for the language model."""
|
839
|
+
|
840
|
+
if target_func is not None:
|
841
|
+
# Decorator usage - use function signature and docstring
|
842
|
+
sig = inspect.signature(target_func)
|
843
|
+
func_name = name or target_func.__name__
|
844
|
+
|
845
|
+
# Get return type from function signature
|
846
|
+
return_type = (
|
847
|
+
sig.return_annotation
|
848
|
+
if sig.return_annotation != inspect.Signature.empty
|
849
|
+
else str
|
850
|
+
)
|
851
|
+
|
852
|
+
# Extract docstring as system instructions
|
853
|
+
system_instructions = instructions or convert_docstring_to_text(
|
854
|
+
target_func
|
855
|
+
)
|
856
|
+
|
857
|
+
# Create parameter schema from function signature
|
858
|
+
parameters_schema = {"type": "object", "properties": {}, "required": []}
|
859
|
+
|
860
|
+
for param_name, param in sig.parameters.items():
|
861
|
+
param_type = (
|
862
|
+
param.annotation
|
863
|
+
if param.annotation != inspect.Parameter.empty
|
864
|
+
else str
|
865
|
+
)
|
866
|
+
|
867
|
+
# Convert type to JSON schema type
|
868
|
+
if param_type == str:
|
869
|
+
json_type = "string"
|
870
|
+
elif param_type == int:
|
871
|
+
json_type = "integer"
|
872
|
+
elif param_type == float:
|
873
|
+
json_type = "number"
|
874
|
+
elif param_type == bool:
|
875
|
+
json_type = "boolean"
|
876
|
+
elif param_type == list:
|
877
|
+
json_type = "array"
|
878
|
+
elif param_type == dict:
|
879
|
+
json_type = "object"
|
880
|
+
else:
|
881
|
+
json_type = "string" # Default fallback
|
882
|
+
|
883
|
+
parameters_schema["properties"][param_name] = {
|
884
|
+
"type": json_type,
|
885
|
+
"description": f"Parameter {param_name} of type {param_type.__name__ if hasattr(param_type, '__name__') else str(param_type)}",
|
886
|
+
}
|
887
|
+
|
888
|
+
if param.default == inspect.Parameter.empty:
|
889
|
+
parameters_schema["required"].append(param_name)
|
890
|
+
|
891
|
+
# Create partial function with model settings
|
892
|
+
partial_func = functools.partial(
|
893
|
+
self._execute_tool_function,
|
894
|
+
target_func=target_func,
|
895
|
+
return_type=return_type,
|
896
|
+
system_instructions=system_instructions,
|
897
|
+
)
|
898
|
+
|
899
|
+
# Handle async functions
|
900
|
+
if asyncio.iscoroutinefunction(target_func):
|
901
|
+
|
902
|
+
async def async_tool_function(**tool_kwargs: Any) -> Any:
|
903
|
+
return await partial_func(**tool_kwargs)
|
904
|
+
|
905
|
+
return BaseTool(
|
906
|
+
name=func_name,
|
907
|
+
description=description
|
908
|
+
or system_instructions
|
909
|
+
or f"Tool for {func_name}",
|
910
|
+
function=async_tool_function,
|
911
|
+
parameters_json_schema=parameters_schema,
|
912
|
+
**kwargs,
|
913
|
+
)
|
914
|
+
else:
|
915
|
+
|
916
|
+
def sync_tool_function(**tool_kwargs: Any) -> Any:
|
917
|
+
return partial_func(**tool_kwargs)
|
918
|
+
|
919
|
+
return BaseTool(
|
920
|
+
name=func_name,
|
921
|
+
description=description
|
922
|
+
or system_instructions
|
923
|
+
or f"Tool for {func_name}",
|
924
|
+
function=sync_tool_function,
|
925
|
+
parameters_json_schema=parameters_schema,
|
926
|
+
**kwargs,
|
927
|
+
)
|
928
|
+
else:
|
929
|
+
# Function usage - create generic tool
|
930
|
+
tool_name = name or f"language_model_{self.model.replace('/', '_')}"
|
931
|
+
tool_description = (
|
932
|
+
description or f"Language model tool using {self.model}"
|
933
|
+
)
|
934
|
+
|
935
|
+
# Create partial function with model settings
|
936
|
+
partial_func = functools.partial(
|
937
|
+
self._execute_generic_tool, system_instructions=instructions
|
938
|
+
)
|
939
|
+
|
940
|
+
def generic_tool_function(
|
941
|
+
input: str, type: Optional[Type[T]] = None, **tool_kwargs: Any
|
942
|
+
) -> Any:
|
943
|
+
"""Generic tool function that runs the language model."""
|
944
|
+
return partial_func(input=input, output_type=type, **tool_kwargs)
|
945
|
+
|
946
|
+
# Generic parameter schema
|
947
|
+
parameters_schema = {
|
948
|
+
"type": "object",
|
949
|
+
"properties": {
|
950
|
+
"input": {
|
951
|
+
"type": "string",
|
952
|
+
"description": "The input text for the language model",
|
953
|
+
},
|
954
|
+
"type": {
|
955
|
+
"type": "string",
|
956
|
+
"description": "Optional output type specification",
|
957
|
+
},
|
958
|
+
},
|
959
|
+
"required": ["input"],
|
960
|
+
}
|
961
|
+
|
962
|
+
return BaseTool(
|
963
|
+
name=tool_name,
|
964
|
+
description=tool_description,
|
965
|
+
function=generic_tool_function,
|
966
|
+
parameters_json_schema=parameters_schema,
|
967
|
+
**kwargs,
|
968
|
+
)
|
969
|
+
|
970
|
+
if func is None:
|
971
|
+
# Called as @model.as_tool() or model.as_tool()
|
972
|
+
return create_tool_wrapper
|
973
|
+
else:
|
974
|
+
# Called as @model.as_tool (without parentheses)
|
975
|
+
return create_tool_wrapper(func)
|
976
|
+
|
977
|
+
def _execute_tool_function(
|
978
|
+
self,
|
979
|
+
target_func: Callable,
|
980
|
+
return_type: Type,
|
981
|
+
system_instructions: str,
|
982
|
+
**kwargs: Any,
|
983
|
+
) -> Any:
|
984
|
+
"""Execute a function-based tool using the language model."""
|
985
|
+
# Format the function call parameters
|
986
|
+
param_text = ", ".join([f"{k}={v}" for k, v in kwargs.items()])
|
987
|
+
input_text = f"Function: {target_func.__name__}({param_text})"
|
988
|
+
|
989
|
+
# Use the language model to generate structured output
|
990
|
+
if return_type != str:
|
991
|
+
response = self.run(
|
992
|
+
messages=[{"role": "user", "content": input_text}],
|
993
|
+
instructions=system_instructions,
|
994
|
+
type=return_type,
|
995
|
+
)
|
996
|
+
else:
|
997
|
+
response = self.run(
|
998
|
+
messages=[{"role": "user", "content": input_text}],
|
999
|
+
instructions=system_instructions,
|
1000
|
+
)
|
1001
|
+
|
1002
|
+
return response.output
|
1003
|
+
|
1004
|
+
def _execute_generic_tool(
|
1005
|
+
self,
|
1006
|
+
input: str,
|
1007
|
+
output_type: Optional[Type] = None,
|
1008
|
+
system_instructions: Optional[str] = None,
|
1009
|
+
**kwargs: Any,
|
1010
|
+
) -> Any:
|
1011
|
+
"""Execute a generic tool using the language model."""
|
1012
|
+
if output_type and output_type != str:
|
1013
|
+
response = self.run(
|
1014
|
+
messages=[{"role": "user", "content": input}],
|
1015
|
+
instructions=system_instructions,
|
1016
|
+
type=output_type,
|
1017
|
+
**kwargs,
|
1018
|
+
)
|
1019
|
+
else:
|
1020
|
+
response = self.run(
|
1021
|
+
messages=[{"role": "user", "content": input}],
|
1022
|
+
instructions=system_instructions,
|
1023
|
+
**kwargs,
|
1024
|
+
)
|
1025
|
+
|
1026
|
+
return response.output
|