fabricatio 0.1.0__py3-none-any.whl → 0.1.2__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.
- fabricatio/__init__.py +24 -18
- fabricatio/config.py +93 -15
- fabricatio/core.py +165 -148
- fabricatio/{logger.py → journal.py} +7 -2
- fabricatio/models/action.py +77 -9
- fabricatio/models/events.py +26 -28
- fabricatio/models/generic.py +277 -121
- fabricatio/models/role.py +44 -8
- fabricatio/models/task.py +224 -0
- fabricatio/models/tool.py +101 -80
- fabricatio/models/utils.py +10 -15
- fabricatio/parser.py +66 -0
- fabricatio/toolboxes/__init__.py +7 -0
- fabricatio/toolboxes/task.py +4 -0
- {fabricatio-0.1.0.dist-info → fabricatio-0.1.2.dist-info}/METADATA +84 -3
- fabricatio-0.1.2.dist-info/RECORD +19 -0
- fabricatio/fs.py +0 -1
- fabricatio-0.1.0.dist-info/RECORD +0 -16
- {fabricatio-0.1.0.dist-info → fabricatio-0.1.2.dist-info}/WHEEL +0 -0
- {fabricatio-0.1.0.dist-info → fabricatio-0.1.2.dist-info}/licenses/LICENSE +0 -0
fabricatio/models/events.py
CHANGED
@@ -2,67 +2,65 @@ from typing import List, Self
|
|
2
2
|
|
3
3
|
from pydantic import BaseModel, ConfigDict, Field
|
4
4
|
|
5
|
+
from fabricatio.config import configs
|
6
|
+
|
5
7
|
|
6
8
|
class Event(BaseModel):
|
9
|
+
"""A class representing an event."""
|
10
|
+
|
7
11
|
model_config = ConfigDict(use_attribute_docstrings=True)
|
8
|
-
delimiter: str = Field(default=".", frozen=True)
|
9
|
-
""" The delimiter used to separate the event name into segments."""
|
10
12
|
|
11
13
|
segments: List[str] = Field(default_factory=list, frozen=True)
|
12
14
|
""" The segments of the namespaces."""
|
13
15
|
|
14
16
|
@classmethod
|
15
|
-
def from_string(cls, event: str
|
16
|
-
"""
|
17
|
-
Create an Event instance from a string.
|
17
|
+
def from_string(cls, event: str) -> Self:
|
18
|
+
"""Create an Event instance from a string.
|
18
19
|
|
19
20
|
Args:
|
20
21
|
event (str): The event string.
|
21
|
-
delimiter (str): The delimiter used to separate the event name into segments.
|
22
22
|
|
23
23
|
Returns:
|
24
24
|
Event: The Event instance.
|
25
25
|
"""
|
26
|
-
return cls(
|
26
|
+
return cls(segments=event.split(configs.pymitter.delimiter))
|
27
27
|
|
28
28
|
def collapse(self) -> str:
|
29
|
-
"""
|
30
|
-
|
31
|
-
"""
|
32
|
-
return self.delimiter.join(self.segments)
|
29
|
+
"""Collapse the event into a string."""
|
30
|
+
return configs.pymitter.delimiter.join(self.segments)
|
33
31
|
|
34
32
|
def clone(self) -> Self:
|
35
|
-
"""
|
36
|
-
|
37
|
-
"""
|
38
|
-
return Event(delimiter=self.delimiter, segments=[segment for segment in self.segments])
|
33
|
+
"""Clone the event."""
|
34
|
+
return Event(segments=list(self.segments))
|
39
35
|
|
40
36
|
def push(self, segment: str) -> Self:
|
41
|
-
"""
|
42
|
-
Push a segment to the event.
|
43
|
-
"""
|
37
|
+
"""Push a segment to the event."""
|
44
38
|
assert segment, "The segment must not be empty."
|
45
|
-
assert
|
39
|
+
assert configs.pymitter.delimiter not in segment, "The segment must not contain the delimiter."
|
46
40
|
|
47
41
|
self.segments.append(segment)
|
48
42
|
return self
|
49
43
|
|
50
44
|
def pop(self) -> str:
|
51
|
-
"""
|
52
|
-
Pop a segment from the event.
|
53
|
-
"""
|
45
|
+
"""Pop a segment from the event."""
|
54
46
|
return self.segments.pop()
|
55
47
|
|
56
48
|
def clear(self) -> Self:
|
57
|
-
"""
|
58
|
-
Clear the event.
|
59
|
-
"""
|
49
|
+
"""Clear the event."""
|
60
50
|
self.segments.clear()
|
61
51
|
return self
|
62
52
|
|
63
53
|
def concat(self, event: Self) -> Self:
|
64
|
-
"""
|
65
|
-
Concatenate another event to this event.
|
66
|
-
"""
|
54
|
+
"""Concatenate another event to this event."""
|
67
55
|
self.segments.extend(event.segments)
|
68
56
|
return self
|
57
|
+
|
58
|
+
def __hash__(self) -> int:
|
59
|
+
"""Return the hash of the event, using the collapsed string."""
|
60
|
+
return hash(self.collapse())
|
61
|
+
|
62
|
+
def __eq__(self, other: Self | str) -> bool:
|
63
|
+
"""Check if the event is equal to another event or a string."""
|
64
|
+
if isinstance(other, Event):
|
65
|
+
other = other.collapse()
|
66
|
+
return self.collapse() == other
|
fabricatio/models/generic.py
CHANGED
@@ -1,18 +1,19 @@
|
|
1
1
|
from asyncio import Queue
|
2
|
-
from typing import Iterable,
|
2
|
+
from typing import Callable, Dict, Iterable, List, Optional, Self
|
3
3
|
|
4
4
|
import litellm
|
5
|
-
|
5
|
+
import orjson
|
6
|
+
from litellm.types.utils import Choices, ModelResponse, StreamingChoices
|
6
7
|
from pydantic import (
|
7
8
|
BaseModel,
|
8
|
-
Field,
|
9
|
-
PositiveInt,
|
10
|
-
NonNegativeInt,
|
11
9
|
ConfigDict,
|
10
|
+
Field,
|
12
11
|
HttpUrl,
|
13
|
-
SecretStr,
|
14
12
|
NonNegativeFloat,
|
13
|
+
NonNegativeInt,
|
14
|
+
PositiveInt,
|
15
15
|
PrivateAttr,
|
16
|
+
SecretStr,
|
16
17
|
)
|
17
18
|
|
18
19
|
from fabricatio.config import configs
|
@@ -20,40 +21,44 @@ from fabricatio.models.utils import Messages
|
|
20
21
|
|
21
22
|
|
22
23
|
class Base(BaseModel):
|
24
|
+
"""Base class for all models with Pydantic configuration."""
|
25
|
+
|
23
26
|
model_config = ConfigDict(use_attribute_docstrings=True)
|
24
27
|
|
25
28
|
|
26
29
|
class WithToDo(Base):
|
30
|
+
"""Class that manages a todo list using an asynchronous queue."""
|
31
|
+
|
27
32
|
_todo: Queue[str] = PrivateAttr(default_factory=Queue)
|
28
33
|
"""
|
29
34
|
The todo list of the current instance.
|
30
35
|
"""
|
31
36
|
|
32
37
|
async def add_todo(self, todo_msg: str) -> Self:
|
33
|
-
"""
|
34
|
-
|
38
|
+
"""Add a todo item to the todo list.
|
39
|
+
|
35
40
|
Args:
|
36
|
-
todo_msg: The todo item to be added to the todo list.
|
41
|
+
todo_msg (str): The todo item to be added to the todo list.
|
37
42
|
|
38
43
|
Returns:
|
39
44
|
Self: The current instance object to support method chaining.
|
40
45
|
"""
|
41
|
-
|
42
46
|
await self._todo.put(todo_msg)
|
43
47
|
return self
|
44
48
|
|
45
49
|
async def get_todo(self) -> str:
|
46
|
-
"""
|
47
|
-
|
50
|
+
"""Get the last todo item from the todo list.
|
51
|
+
|
48
52
|
Returns:
|
49
53
|
str: The last todo item from the todo list.
|
50
|
-
|
51
54
|
"""
|
52
55
|
# Pop the last todo item from the todo list
|
53
56
|
return await self._todo.get()
|
54
57
|
|
55
58
|
|
56
59
|
class Named(Base):
|
60
|
+
"""Class that includes a name attribute."""
|
61
|
+
|
57
62
|
name: str = Field(frozen=True)
|
58
63
|
"""
|
59
64
|
Name of the object.
|
@@ -61,6 +66,8 @@ class Named(Base):
|
|
61
66
|
|
62
67
|
|
63
68
|
class Described(Base):
|
69
|
+
"""Class that includes a description attribute."""
|
70
|
+
|
64
71
|
description: str = Field(default="", frozen=True)
|
65
72
|
"""
|
66
73
|
Description of the object.
|
@@ -68,11 +75,12 @@ class Described(Base):
|
|
68
75
|
|
69
76
|
|
70
77
|
class WithBriefing(Named, Described):
|
78
|
+
"""Class that provides a briefing based on the name and description."""
|
71
79
|
|
72
80
|
@property
|
73
81
|
def briefing(self) -> str:
|
74
|
-
"""
|
75
|
-
|
82
|
+
"""Get the briefing of the object.
|
83
|
+
|
76
84
|
Returns:
|
77
85
|
str: The briefing of the object.
|
78
86
|
"""
|
@@ -80,6 +88,8 @@ class WithBriefing(Named, Described):
|
|
80
88
|
|
81
89
|
|
82
90
|
class Memorable(Base):
|
91
|
+
"""Class that manages a memory list with a maximum size."""
|
92
|
+
|
83
93
|
memory: List[str] = Field(default_factory=list)
|
84
94
|
"""
|
85
95
|
Memory list.
|
@@ -90,19 +100,13 @@ class Memorable(Base):
|
|
90
100
|
"""
|
91
101
|
|
92
102
|
def add_memory(self, memories: str | Iterable[str]) -> Self:
|
93
|
-
"""
|
94
|
-
Add memory items to the memory list.
|
95
|
-
|
96
|
-
This method appends memory items to the memory list of the current instance.
|
103
|
+
"""Add memory items to the memory list.
|
97
104
|
|
98
|
-
|
99
|
-
|
105
|
+
Args:
|
106
|
+
memories (str | Iterable[str]): A single memory item as a string or multiple memory items as an iterable.
|
100
107
|
|
101
108
|
Returns:
|
102
|
-
|
103
|
-
|
104
|
-
This method design allows users to add memory items to the memory list
|
105
|
-
through a unified interface, enhancing code usability and extensibility.
|
109
|
+
Self: The current instance object to support method chaining.
|
106
110
|
"""
|
107
111
|
# Convert a single memory item to a list
|
108
112
|
if isinstance(memories, str):
|
@@ -111,43 +115,31 @@ class Memorable(Base):
|
|
111
115
|
self.memory.extend(memories)
|
112
116
|
# Limit the memory list size if the maximum size is set
|
113
117
|
if self.memory_max_size > 0:
|
114
|
-
self.memory = self.memory[-self.memory_max_size:]
|
118
|
+
self.memory = self.memory[-self.memory_max_size :]
|
115
119
|
# Return the current instance object to support method chaining
|
116
120
|
return self
|
117
121
|
|
118
122
|
def top_memories(self, n: PositiveInt = 1) -> List[str]:
|
119
|
-
"""
|
120
|
-
Get the top memory items from the memory list.
|
121
|
-
|
122
|
-
This method returns the top memory items from the memory list of the current instance.
|
123
|
+
"""Get the top memory items from the memory list.
|
123
124
|
|
124
|
-
|
125
|
-
|
125
|
+
Args:
|
126
|
+
n (PositiveInt): The number of top memory items to return.
|
126
127
|
|
127
128
|
Returns:
|
128
|
-
|
129
|
-
|
130
|
-
This method design allows users to get the top memory items from the memory list
|
131
|
-
through a unified interface, enhancing code usability and extensibility.
|
129
|
+
List[str]: The top memory items from the memory list.
|
132
130
|
"""
|
133
131
|
# Get the top memory items from the memory list
|
134
132
|
return self.memory[-n:]
|
135
133
|
|
136
134
|
def top_memories_as_string(self, n: PositiveInt = 1, separator: str = "\n\n") -> str:
|
137
|
-
"""
|
138
|
-
Get the memory items as a string.
|
139
|
-
|
140
|
-
This method returns the memory items as a string from the memory list of the current instance.
|
135
|
+
"""Get the memory items as a string.
|
141
136
|
|
142
|
-
|
143
|
-
|
144
|
-
|
137
|
+
Args:
|
138
|
+
n (PositiveInt): The number of memory items to return.
|
139
|
+
separator (str): The separator to join memory items.
|
145
140
|
|
146
141
|
Returns:
|
147
|
-
|
148
|
-
|
149
|
-
This method design allows users to get the memory items as a string from the memory list
|
150
|
-
through a unified interface, enhancing code usability and extensibility.
|
142
|
+
str: The memory items as a string.
|
151
143
|
"""
|
152
144
|
# Get the top memory items from the memory list
|
153
145
|
memories = self.top_memories(n)
|
@@ -155,19 +147,10 @@ class Memorable(Base):
|
|
155
147
|
return separator.join(memories)
|
156
148
|
|
157
149
|
def clear_memories(self) -> Self:
|
158
|
-
"""
|
159
|
-
Clear all memory items.
|
160
|
-
|
161
|
-
This method clears all memory items from the memory list of the current instance.
|
162
|
-
|
163
|
-
Parameters:
|
164
|
-
- self: The current instance object.
|
150
|
+
"""Clear all memory items.
|
165
151
|
|
166
152
|
Returns:
|
167
|
-
|
168
|
-
|
169
|
-
This method design allows users to clear all memory items from the memory list
|
170
|
-
through a unified interface, enhancing code usability and extensibility.
|
153
|
+
Self: The current instance object to support method chaining.
|
171
154
|
"""
|
172
155
|
# Clear all memory items from the memory list
|
173
156
|
self.memory.clear()
|
@@ -176,124 +159,143 @@ class Memorable(Base):
|
|
176
159
|
|
177
160
|
|
178
161
|
class LLMUsage(Base):
|
179
|
-
|
162
|
+
"""Class that manages LLM (Large Language Model) usage parameters and methods."""
|
163
|
+
|
164
|
+
llm_api_endpoint: Optional[HttpUrl] = None
|
180
165
|
"""
|
181
166
|
The OpenAI API endpoint.
|
182
167
|
"""
|
183
168
|
|
184
|
-
llm_api_key: SecretStr =
|
169
|
+
llm_api_key: Optional[SecretStr] = None
|
185
170
|
"""
|
186
171
|
The OpenAI API key.
|
187
172
|
"""
|
188
173
|
|
189
|
-
llm_timeout: PositiveInt =
|
174
|
+
llm_timeout: Optional[PositiveInt] = None
|
190
175
|
"""
|
191
176
|
The timeout of the LLM model.
|
192
177
|
"""
|
193
178
|
|
194
|
-
llm_max_retries: PositiveInt =
|
179
|
+
llm_max_retries: Optional[PositiveInt] = None
|
195
180
|
"""
|
196
181
|
The maximum number of retries.
|
197
182
|
"""
|
198
183
|
|
199
|
-
llm_model: str =
|
184
|
+
llm_model: Optional[str] = None
|
200
185
|
"""
|
201
186
|
The LLM model name.
|
202
187
|
"""
|
203
188
|
|
204
|
-
llm_temperature: NonNegativeFloat =
|
189
|
+
llm_temperature: Optional[NonNegativeFloat] = None
|
205
190
|
"""
|
206
191
|
The temperature of the LLM model.
|
207
192
|
"""
|
208
193
|
|
209
|
-
llm_stop_sign: str =
|
194
|
+
llm_stop_sign: Optional[str | List[str]] = None
|
210
195
|
"""
|
211
196
|
The stop sign of the LLM model.
|
212
197
|
"""
|
213
198
|
|
214
|
-
llm_top_p: NonNegativeFloat =
|
199
|
+
llm_top_p: Optional[NonNegativeFloat] = None
|
215
200
|
"""
|
216
201
|
The top p of the LLM model.
|
217
202
|
"""
|
218
203
|
|
219
|
-
llm_generation_count: PositiveInt =
|
204
|
+
llm_generation_count: Optional[PositiveInt] = None
|
220
205
|
"""
|
221
206
|
The number of generations to generate.
|
222
207
|
"""
|
223
208
|
|
224
|
-
llm_stream: bool =
|
209
|
+
llm_stream: Optional[bool] = None
|
225
210
|
"""
|
226
211
|
Whether to stream the LLM model's response.
|
227
212
|
"""
|
228
213
|
|
229
|
-
llm_max_tokens: PositiveInt =
|
214
|
+
llm_max_tokens: Optional[PositiveInt] = None
|
230
215
|
"""
|
231
216
|
The maximum number of tokens to generate.
|
232
217
|
"""
|
233
218
|
|
234
|
-
def model_post_init(self, __context: Any) -> None:
|
235
|
-
litellm.api_key = self.llm_api_key.get_secret_value()
|
236
|
-
litellm.api_base = self.llm_api_endpoint.unicode_string()
|
237
|
-
|
238
219
|
async def aquery(
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
220
|
+
self,
|
221
|
+
messages: List[Dict[str, str]],
|
222
|
+
model: str | None = None,
|
223
|
+
temperature: NonNegativeFloat | None = None,
|
224
|
+
stop: str | List[str] | None = None,
|
225
|
+
top_p: NonNegativeFloat | None = None,
|
226
|
+
max_tokens: PositiveInt | None = None,
|
227
|
+
n: PositiveInt | None = None,
|
228
|
+
stream: bool | None = None,
|
229
|
+
timeout: PositiveInt | None = None,
|
230
|
+
max_retries: PositiveInt | None = None,
|
250
231
|
) -> ModelResponse:
|
251
|
-
"""
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
- max_retries (PositiveInt | None): The maximum number of retries in case of failure.
|
232
|
+
"""Asynchronously queries the language model to generate a response based on the provided messages and parameters.
|
233
|
+
|
234
|
+
Args:
|
235
|
+
messages (List[Dict[str, str]]): A list of messages, where each message is a dictionary containing the role and content of the message.
|
236
|
+
model (str | None): The name of the model to use. If not provided, the default model will be used.
|
237
|
+
temperature (NonNegativeFloat | None): Controls the randomness of the output. Lower values make the output more deterministic.
|
238
|
+
stop (str | None): A sequence at which to stop the generation of the response.
|
239
|
+
top_p (NonNegativeFloat | None): Controls the diversity of the output through nucleus sampling.
|
240
|
+
max_tokens (PositiveInt | None): The maximum number of tokens to generate in the response.
|
241
|
+
n (PositiveInt | None): The number of responses to generate.
|
242
|
+
stream (bool | None): Whether to receive the response in a streaming fashion.
|
243
|
+
timeout (PositiveInt | None): The timeout duration for the request.
|
244
|
+
max_retries (PositiveInt | None): The maximum number of retries in case of failure.
|
265
245
|
|
266
246
|
Returns:
|
267
|
-
|
247
|
+
ModelResponse: An object containing the generated response and other metadata from the model.
|
268
248
|
"""
|
269
249
|
# Call the underlying asynchronous completion function with the provided and default parameters
|
270
250
|
return await litellm.acompletion(
|
271
251
|
messages=messages,
|
272
|
-
model=model or self.llm_model,
|
273
|
-
temperature=temperature or self.llm_temperature,
|
274
|
-
stop=stop or self.llm_stop_sign,
|
275
|
-
top_p=top_p or self.llm_top_p,
|
276
|
-
max_tokens=max_tokens or self.llm_max_tokens,
|
277
|
-
n=n or self.llm_generation_count,
|
278
|
-
stream=stream or self.llm_stream,
|
279
|
-
timeout=timeout or self.llm_timeout,
|
280
|
-
max_retries=max_retries or self.llm_max_retries,
|
252
|
+
model=model or self.llm_model or configs.llm.model,
|
253
|
+
temperature=temperature or self.llm_temperature or configs.llm.temperature,
|
254
|
+
stop=stop or self.llm_stop_sign or configs.llm.stop_sign,
|
255
|
+
top_p=top_p or self.llm_top_p or configs.llm.top_p,
|
256
|
+
max_tokens=max_tokens or self.llm_max_tokens or configs.llm.max_tokens,
|
257
|
+
n=n or self.llm_generation_count or configs.llm.generation_count,
|
258
|
+
stream=stream or self.llm_stream or configs.llm.stream,
|
259
|
+
timeout=timeout or self.llm_timeout or configs.llm.timeout,
|
260
|
+
max_retries=max_retries or self.llm_max_retries or configs.llm.max_retries,
|
261
|
+
api_key=self.llm_api_key.get_secret_value() if self.llm_api_key else configs.llm.api_key.get_secret_value(),
|
262
|
+
base_url=self.llm_api_endpoint.unicode_string()
|
263
|
+
if self.llm_api_endpoint
|
264
|
+
else configs.llm.api_endpoint.unicode_string(),
|
281
265
|
)
|
282
266
|
|
283
|
-
async def
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
294
|
-
|
295
|
-
|
267
|
+
async def ainvoke(
|
268
|
+
self,
|
269
|
+
question: str,
|
270
|
+
system_message: str = "",
|
271
|
+
model: str | None = None,
|
272
|
+
temperature: NonNegativeFloat | None = None,
|
273
|
+
stop: str | List[str] | None = None,
|
274
|
+
top_p: NonNegativeFloat | None = None,
|
275
|
+
max_tokens: PositiveInt | None = None,
|
276
|
+
n: PositiveInt | None = None,
|
277
|
+
stream: bool | None = None,
|
278
|
+
timeout: PositiveInt | None = None,
|
279
|
+
max_retries: PositiveInt | None = None,
|
296
280
|
) -> List[Choices | StreamingChoices]:
|
281
|
+
"""Asynchronously invokes the language model with a question and optional system message.
|
282
|
+
|
283
|
+
Args:
|
284
|
+
question (str): The question to ask the model.
|
285
|
+
system_message (str): The system message to provide context to the model.
|
286
|
+
model (str | None): The name of the model to use. If not provided, the default model will be used.
|
287
|
+
temperature (NonNegativeFloat | None): Controls the randomness of the output. Lower values make the output more deterministic.
|
288
|
+
stop (str | None): A sequence at which to stop the generation of the response.
|
289
|
+
top_p (NonNegativeFloat | None): Controls the diversity of the output through nucleus sampling.
|
290
|
+
max_tokens (PositiveInt | None): The maximum number of tokens to generate in the response.
|
291
|
+
n (PositiveInt | None): The number of responses to generate.
|
292
|
+
stream (bool | None): Whether to receive the response in a streaming fashion.
|
293
|
+
timeout (PositiveInt | None): The timeout duration for the request.
|
294
|
+
max_retries (PositiveInt | None): The maximum number of retries in case of failure.
|
295
|
+
|
296
|
+
Returns:
|
297
|
+
List[Choices | StreamingChoices]: A list of choices or streaming choices from the model response.
|
298
|
+
"""
|
297
299
|
return (
|
298
300
|
await self.aquery(
|
299
301
|
messages=Messages().add_system_message(system_message).add_user_message(question),
|
@@ -308,3 +310,157 @@ class LLMUsage(Base):
|
|
308
310
|
max_retries=max_retries,
|
309
311
|
)
|
310
312
|
).choices
|
313
|
+
|
314
|
+
async def aask(
|
315
|
+
self,
|
316
|
+
question: str,
|
317
|
+
system_message: str = "",
|
318
|
+
model: str | None = None,
|
319
|
+
temperature: NonNegativeFloat | None = None,
|
320
|
+
stop: str | List[str] | None = None,
|
321
|
+
top_p: NonNegativeFloat | None = None,
|
322
|
+
max_tokens: PositiveInt | None = None,
|
323
|
+
stream: bool | None = None,
|
324
|
+
timeout: PositiveInt | None = None,
|
325
|
+
max_retries: PositiveInt | None = None,
|
326
|
+
) -> str:
|
327
|
+
"""Asynchronously asks the language model a question and returns the response content.
|
328
|
+
|
329
|
+
Args:
|
330
|
+
question (str): The question to ask the model.
|
331
|
+
system_message (str): The system message to provide context to the model.
|
332
|
+
model (str | None): The name of the model to use. If not provided, the default model will be used.
|
333
|
+
temperature (NonNegativeFloat | None): Controls the randomness of the output. Lower values make the output more deterministic.
|
334
|
+
stop (str | None): A sequence at which to stop the generation of the response.
|
335
|
+
top_p (NonNegativeFloat | None): Controls the diversity of the output through nucleus sampling.
|
336
|
+
max_tokens (PositiveInt | None): The maximum number of tokens to generate in the response.
|
337
|
+
stream (bool | None): Whether to receive the response in a streaming fashion.
|
338
|
+
timeout (PositiveInt | None): The timeout duration for the request.
|
339
|
+
max_retries (PositiveInt | None): The maximum number of retries in case of failure.
|
340
|
+
|
341
|
+
Returns:
|
342
|
+
str: The content of the model's response message.
|
343
|
+
"""
|
344
|
+
return (
|
345
|
+
(
|
346
|
+
await self.ainvoke(
|
347
|
+
n=1,
|
348
|
+
question=question,
|
349
|
+
system_message=system_message,
|
350
|
+
model=model,
|
351
|
+
temperature=temperature,
|
352
|
+
stop=stop,
|
353
|
+
top_p=top_p,
|
354
|
+
max_tokens=max_tokens,
|
355
|
+
stream=stream,
|
356
|
+
timeout=timeout,
|
357
|
+
max_retries=max_retries,
|
358
|
+
)
|
359
|
+
)
|
360
|
+
.pop()
|
361
|
+
.message.content
|
362
|
+
)
|
363
|
+
|
364
|
+
async def aask_validate[T](
|
365
|
+
self,
|
366
|
+
question: str,
|
367
|
+
validator: Callable[[str], T | None],
|
368
|
+
max_validations: PositiveInt = 2,
|
369
|
+
system_message: str = "",
|
370
|
+
model: str | None = None,
|
371
|
+
temperature: NonNegativeFloat | None = None,
|
372
|
+
stop: str | List[str] | None = None,
|
373
|
+
top_p: NonNegativeFloat | None = None,
|
374
|
+
max_tokens: PositiveInt | None = None,
|
375
|
+
stream: bool | None = None,
|
376
|
+
timeout: PositiveInt | None = None,
|
377
|
+
max_retries: PositiveInt | None = None,
|
378
|
+
) -> T:
|
379
|
+
"""Asynchronously ask a question and validate the response using a given validator.
|
380
|
+
|
381
|
+
Args:
|
382
|
+
question (str): The question to ask.
|
383
|
+
validator (Callable[[str], T | None]): A function to validate the response.
|
384
|
+
max_validations (PositiveInt): Maximum number of validation attempts.
|
385
|
+
system_message (str): System message to include in the request.
|
386
|
+
model (str | None): The model to use for the request.
|
387
|
+
temperature (NonNegativeFloat | None): Temperature setting for the request.
|
388
|
+
stop (str | None): Stop sequence for the request.
|
389
|
+
top_p (NonNegativeFloat | None): Top-p sampling parameter.
|
390
|
+
max_tokens (PositiveInt | None): Maximum number of tokens in the response.
|
391
|
+
stream (bool | None): Whether to stream the response.
|
392
|
+
timeout (PositiveInt | None): Timeout for the request.
|
393
|
+
max_retries (PositiveInt | None): Maximum number of retries for the request.
|
394
|
+
|
395
|
+
Returns:
|
396
|
+
T: The validated response.
|
397
|
+
|
398
|
+
Raises:
|
399
|
+
ValueError: If the response fails to validate after the maximum number of attempts.
|
400
|
+
"""
|
401
|
+
for _ in range(max_validations):
|
402
|
+
if (
|
403
|
+
response := await self.aask(
|
404
|
+
question,
|
405
|
+
system_message,
|
406
|
+
model,
|
407
|
+
temperature,
|
408
|
+
stop,
|
409
|
+
top_p,
|
410
|
+
max_tokens,
|
411
|
+
stream,
|
412
|
+
timeout,
|
413
|
+
max_retries,
|
414
|
+
)
|
415
|
+
) and (validated := validator(response)):
|
416
|
+
return validated
|
417
|
+
raise ValueError("Failed to validate the response.")
|
418
|
+
|
419
|
+
def fallback_to(self, other: "LLMUsage") -> Self:
|
420
|
+
"""Fallback to another instance's attribute values if the current instance's attributes are None.
|
421
|
+
|
422
|
+
Args:
|
423
|
+
other (LLMUsage): Another instance from which to copy attribute values.
|
424
|
+
|
425
|
+
Returns:
|
426
|
+
Self: The current instance, allowing for method chaining.
|
427
|
+
"""
|
428
|
+
# Define the list of attribute names to check and potentially copy
|
429
|
+
attr_names = [
|
430
|
+
"llm_api_endpoint",
|
431
|
+
"llm_api_key",
|
432
|
+
"llm_model",
|
433
|
+
"llm_stop_sign",
|
434
|
+
"llm_temperature",
|
435
|
+
"llm_top_p",
|
436
|
+
"llm_generation_count",
|
437
|
+
"llm_stream",
|
438
|
+
"llm_max_tokens",
|
439
|
+
"llm_timeout",
|
440
|
+
"llm_max_retries",
|
441
|
+
]
|
442
|
+
|
443
|
+
# Iterate over the attribute names and copy values from 'other' to 'self' where applicable
|
444
|
+
for attr_name in attr_names:
|
445
|
+
# Copy the attribute value from 'other' to 'self' only if 'self' has None and 'other' has a non-None value
|
446
|
+
if getattr(self, attr_name) is None and (attr := getattr(other, attr_name)) is not None:
|
447
|
+
setattr(self, attr_name, attr)
|
448
|
+
|
449
|
+
# Return the current instance to allow for method chaining
|
450
|
+
return self
|
451
|
+
|
452
|
+
|
453
|
+
class WithJsonExample(Base):
|
454
|
+
"""Class that provides a JSON schema for the model."""
|
455
|
+
|
456
|
+
@classmethod
|
457
|
+
def json_example(cls) -> str:
|
458
|
+
"""Return a JSON example for the model.
|
459
|
+
|
460
|
+
Returns:
|
461
|
+
str: A JSON example for the model.
|
462
|
+
"""
|
463
|
+
return orjson.dumps(
|
464
|
+
{field_name: field_info.description for field_name, field_info in cls.model_fields.items()},
|
465
|
+
option=orjson.OPT_INDENT_2 | orjson.OPT_SORT_KEYS,
|
466
|
+
).decode()
|