agently 4.0.5.3__tar.gz → 4.0.6.2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. {agently-4.0.5.3 → agently-4.0.6.2}/PKG-INFO +2 -2
  2. {agently-4.0.5.3 → agently-4.0.6.2}/agently/_default_settings.yaml +12 -0
  3. {agently-4.0.5.3 → agently-4.0.6.2}/agently/builtins/agent_extensions/ConfigurePromptExtension.py +4 -2
  4. {agently-4.0.5.3 → agently-4.0.6.2}/agently/builtins/agent_extensions/KeyWaiterExtension.py +2 -1
  5. {agently-4.0.5.3 → agently-4.0.6.2}/agently/builtins/agent_extensions/ToolExtension.py +1 -1
  6. {agently-4.0.5.3 → agently-4.0.6.2}/agently/builtins/plugins/ModelRequester/OpenAICompatible.py +150 -44
  7. {agently-4.0.5.3 → agently-4.0.6.2}/agently/builtins/plugins/PromptGenerator/AgentlyPromptGenerator.py +105 -16
  8. {agently-4.0.5.3 → agently-4.0.6.2}/agently/builtins/plugins/ResponseParser/AgentlyResponseParser.py +57 -17
  9. {agently-4.0.5.3 → agently-4.0.6.2}/agently/builtins/tools/Search.py +87 -5
  10. {agently-4.0.5.3 → agently-4.0.6.2}/agently/core/Agent.py +39 -9
  11. {agently-4.0.5.3 → agently-4.0.6.2}/agently/core/ModelRequest.py +39 -27
  12. {agently-4.0.5.3 → agently-4.0.6.2}/agently/core/Prompt.py +1 -1
  13. agently-4.0.6.2/agently/integrations/chromadb.py +86 -0
  14. {agently-4.0.5.3 → agently-4.0.6.2}/agently/types/data/prompt.py +4 -0
  15. {agently-4.0.5.3 → agently-4.0.6.2}/agently/types/data/response.py +3 -1
  16. {agently-4.0.5.3 → agently-4.0.6.2}/agently/types/plugins/ResponseParser.py +16 -16
  17. {agently-4.0.5.3 → agently-4.0.6.2}/agently/utils/DataFormatter.py +67 -9
  18. {agently-4.0.5.3 → agently-4.0.6.2}/agently/utils/FunctionShifter.py +60 -10
  19. {agently-4.0.5.3 → agently-4.0.6.2}/agently/utils/RuntimeData.py +1 -1
  20. {agently-4.0.5.3 → agently-4.0.6.2}/agently/utils/Storage.py +8 -3
  21. {agently-4.0.5.3 → agently-4.0.6.2}/pyproject.toml +3 -3
  22. {agently-4.0.5.3 → agently-4.0.6.2}/LICENSE +0 -0
  23. {agently-4.0.5.3 → agently-4.0.6.2}/README.md +0 -0
  24. {agently-4.0.5.3 → agently-4.0.6.2}/agently/__init__.py +0 -0
  25. {agently-4.0.5.3 → agently-4.0.6.2}/agently/_default_init.py +0 -0
  26. {agently-4.0.5.3 → agently-4.0.6.2}/agently/base.py +0 -0
  27. {agently-4.0.5.3 → agently-4.0.6.2}/agently/builtins/agent_extensions/AutoFuncExtension.py +0 -0
  28. {agently-4.0.5.3 → agently-4.0.6.2}/agently/builtins/agent_extensions/ChatSessionExtension.py +0 -0
  29. {agently-4.0.5.3 → agently-4.0.6.2}/agently/builtins/agent_extensions/__init__.py +0 -0
  30. {agently-4.0.5.3 → agently-4.0.6.2}/agently/builtins/hookers/ConsoleHooker.py +0 -0
  31. {agently-4.0.5.3 → agently-4.0.6.2}/agently/builtins/hookers/PureLoggerHooker.py +0 -0
  32. {agently-4.0.5.3 → agently-4.0.6.2}/agently/builtins/hookers/SystemMessageHooker.py +0 -0
  33. {agently-4.0.5.3 → agently-4.0.6.2}/agently/builtins/plugins/ToolManager/AgentlyToolManager.py +0 -0
  34. {agently-4.0.5.3 → agently-4.0.6.2}/agently/builtins/plugins/__init__.py +0 -0
  35. {agently-4.0.5.3 → agently-4.0.6.2}/agently/builtins/tools/Browse.py +0 -0
  36. {agently-4.0.5.3 → agently-4.0.6.2}/agently/builtins/tools/__init__.py +0 -0
  37. {agently-4.0.5.3 → agently-4.0.6.2}/agently/core/EventCenter.py +0 -0
  38. {agently-4.0.5.3 → agently-4.0.6.2}/agently/core/ExtensionHandlers.py +0 -0
  39. {agently-4.0.5.3 → agently-4.0.6.2}/agently/core/PluginManager.py +0 -0
  40. {agently-4.0.5.3 → agently-4.0.6.2}/agently/core/Tool.py +0 -0
  41. {agently-4.0.5.3 → agently-4.0.6.2}/agently/core/TriggerFlow/BluePrint.py +0 -0
  42. {agently-4.0.5.3 → agently-4.0.6.2}/agently/core/TriggerFlow/Chunk.py +0 -0
  43. {agently-4.0.5.3 → agently-4.0.6.2}/agently/core/TriggerFlow/Execution.py +0 -0
  44. {agently-4.0.5.3 → agently-4.0.6.2}/agently/core/TriggerFlow/Process.py +0 -0
  45. {agently-4.0.5.3 → agently-4.0.6.2}/agently/core/TriggerFlow/TriggerFlow.py +0 -0
  46. {agently-4.0.5.3 → agently-4.0.6.2}/agently/core/TriggerFlow/__init__.py +0 -0
  47. {agently-4.0.5.3 → agently-4.0.6.2}/agently/core/TriggerFlow/process/BaseProcess.py +0 -0
  48. {agently-4.0.5.3 → agently-4.0.6.2}/agently/core/TriggerFlow/process/ForEachProcess.py +0 -0
  49. {agently-4.0.5.3 → agently-4.0.6.2}/agently/core/TriggerFlow/process/MatchCaseProcess.py +0 -0
  50. {agently-4.0.5.3 → agently-4.0.6.2}/agently/core/TriggerFlow/process/__init__.py +0 -0
  51. {agently-4.0.5.3 → agently-4.0.6.2}/agently/core/__init__.py +0 -0
  52. {agently-4.0.5.3 → agently-4.0.6.2}/agently/types/__init__.py +0 -0
  53. {agently-4.0.5.3 → agently-4.0.6.2}/agently/types/data/__init__.py +0 -0
  54. {agently-4.0.5.3 → agently-4.0.6.2}/agently/types/data/event.py +0 -0
  55. {agently-4.0.5.3 → agently-4.0.6.2}/agently/types/data/request.py +0 -0
  56. {agently-4.0.5.3 → agently-4.0.6.2}/agently/types/data/serializable.py +0 -0
  57. {agently-4.0.5.3 → agently-4.0.6.2}/agently/types/data/tool.py +0 -0
  58. {agently-4.0.5.3 → agently-4.0.6.2}/agently/types/plugins/EventHooker.py +0 -0
  59. {agently-4.0.5.3 → agently-4.0.6.2}/agently/types/plugins/ModelRequester.py +0 -0
  60. {agently-4.0.5.3 → agently-4.0.6.2}/agently/types/plugins/PromptGenerator.py +0 -0
  61. {agently-4.0.5.3 → agently-4.0.6.2}/agently/types/plugins/ToolManager.py +0 -0
  62. {agently-4.0.5.3 → agently-4.0.6.2}/agently/types/plugins/__init__.py +0 -0
  63. {agently-4.0.5.3 → agently-4.0.6.2}/agently/types/plugins/base.py +0 -0
  64. {agently-4.0.5.3 → agently-4.0.6.2}/agently/types/trigger_flow/__init__.py +0 -0
  65. {agently-4.0.5.3 → agently-4.0.6.2}/agently/types/trigger_flow/trigger_flow.py +0 -0
  66. {agently-4.0.5.3 → agently-4.0.6.2}/agently/utils/DataLocator.py +0 -0
  67. {agently-4.0.5.3 → agently-4.0.6.2}/agently/utils/DataPathBuilder.py +0 -0
  68. {agently-4.0.5.3 → agently-4.0.6.2}/agently/utils/GeneratorConsumer.py +0 -0
  69. {agently-4.0.5.3 → agently-4.0.6.2}/agently/utils/LazyImport.py +0 -0
  70. {agently-4.0.5.3 → agently-4.0.6.2}/agently/utils/Logger.py +0 -0
  71. {agently-4.0.5.3 → agently-4.0.6.2}/agently/utils/Messenger.py +0 -0
  72. {agently-4.0.5.3 → agently-4.0.6.2}/agently/utils/SerializableRuntimeData.py +0 -0
  73. {agently-4.0.5.3 → agently-4.0.6.2}/agently/utils/Settings.py +0 -0
  74. {agently-4.0.5.3 → agently-4.0.6.2}/agently/utils/StreamingJSONCompleter.py +0 -0
  75. {agently-4.0.5.3 → agently-4.0.6.2}/agently/utils/StreamingJSONParser.py +0 -0
  76. {agently-4.0.5.3 → agently-4.0.6.2}/agently/utils/__init__.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agently
3
- Version: 4.0.5.3
3
+ Version: 4.0.6.2
4
4
  Summary:
5
5
  License: Apache-2.0
6
6
  License-File: LICENSE
@@ -19,9 +19,9 @@ Requires-Dist: greenlet (>=3.2.3,<4.0.0)
19
19
  Requires-Dist: httpx (>=0.28.1,<0.29.0)
20
20
  Requires-Dist: httpx-sse (>=0.4.1,<0.5.0)
21
21
  Requires-Dist: json5 (>=0.12.0,<0.13.0)
22
+ Requires-Dist: packaging (>=25.0,<26.0)
22
23
  Requires-Dist: pydantic (>=2.11.7,<3.0.0)
23
24
  Requires-Dist: pyyaml (>=6.0.2,<7.0.0)
24
- Requires-Dist: sqlmodel (>=0.0.24,<0.0.25)
25
25
  Requires-Dist: stamina (>=25.1.0,<26.0.0)
26
26
  Requires-Dist: toml (>=0.10.2,<0.11.0)
27
27
  Description-Content-Type: text/markdown
@@ -22,6 +22,18 @@ prompt:
22
22
  assistant: assistant
23
23
  user: user
24
24
  _: assistant
25
+ prompt_title_mapping:
26
+ system: "SYSTEM"
27
+ developer: "DEVELOPER DIRECTIONS"
28
+ chat_history: "CHAT HISTORY"
29
+ info: "INFO"
30
+ tools: "TOOLS"
31
+ action_results: "ACTION RESULTS"
32
+ instruct: "INSTRUCT"
33
+ examples: "EXAMPLES"
34
+ input: "INPUT"
35
+ output: "OUTPUT"
36
+ output_requirement: "OUTPUT REQUIREMENT"
25
37
  response:
26
38
  streaming_parse: False
27
39
  streaming_parse_path_style: dot
@@ -38,11 +38,13 @@ class ConfigurePromptExtension(BaseAgent):
38
38
  output_desc = output_prompt_value[".desc"]
39
39
  if output_type or output_desc:
40
40
  return (
41
- output_type if output_type is not None else Any,
41
+ self._generate_output_value(output_type) if output_type is not None else Any,
42
42
  output_desc,
43
43
  )
44
44
  else:
45
- return output_prompt_value
45
+ return {key: self._generate_output_value(value) for key, value in output_prompt_value.items()}
46
+ elif isinstance(output_prompt_value, list):
47
+ return [self._generate_output_value(item) for item in output_prompt_value]
46
48
  else:
47
49
  return output_prompt_value
48
50
 
@@ -19,6 +19,7 @@ from typing import Any, Callable
19
19
  from agently.core import BaseAgent
20
20
  from agently.utils import FunctionShifter, GeneratorConsumer
21
21
 
22
+
22
23
  class KeyWaiterExtension(BaseAgent):
23
24
  def __init__(self, *args, **kwargs):
24
25
  super().__init__(*args, **kwargs)
@@ -48,7 +49,7 @@ class KeyWaiterExtension(BaseAgent):
48
49
 
49
50
  def __get_consumer(self):
50
51
  response = self.get_response()
51
- return GeneratorConsumer(response.get_async_generator(content="instant"))
52
+ return GeneratorConsumer(response.get_async_generator(type="instant"))
52
53
 
53
54
  async def async_get_key_result(
54
55
  self,
@@ -119,7 +119,7 @@ class ToolExtension(BaseAgent):
119
119
  },
120
120
  )
121
121
  tool_judgement_response = tool_judgement_request.get_response()
122
- tool_judgement_result = tool_judgement_response.get_async_generator(content="instant")
122
+ tool_judgement_result = tool_judgement_response.get_async_generator(type="instant")
123
123
  async for instant in tool_judgement_result:
124
124
  if instant.path == "use_tool" and instant.is_complete:
125
125
  if instant.value is False:
@@ -46,6 +46,7 @@ class ContentMapping(TypedDict):
46
46
  id: str | None
47
47
  role: str | None
48
48
  delta: str | None
49
+ tool_calls: str | None
49
50
  done: str | None
50
51
  usage: str | None
51
52
  finish_reason: str | None
@@ -84,6 +85,8 @@ class OpenAICompatible(ModelRequester):
84
85
  "$mappings": {
85
86
  "path_mappings": {
86
87
  "OpenAICompatible": "plugins.ModelRequester.OpenAICompatible",
88
+ "OpenAI": "plugins.ModelRequester.OpenAICompatible",
89
+ "OAIClient": "plugins.ModelRequester.OpenAICompatible",
87
90
  },
88
91
  },
89
92
  "model_type": "chat",
@@ -98,6 +101,7 @@ class OpenAICompatible(ModelRequester):
98
101
  "proxy": None,
99
102
  "request_options": {},
100
103
  "base_url": "https://api.openai.com/v1",
104
+ "full_url": None,
101
105
  "path_mapping": {
102
106
  "chat": "/chat/completions",
103
107
  "completions": "/completions",
@@ -111,11 +115,11 @@ class OpenAICompatible(ModelRequester):
111
115
  "id": "id",
112
116
  "role": "choices[0].delta.role",
113
117
  "delta": "choices[0].delta.content",
118
+ "tool_calls": "choices[0].delta.tool_calls",
114
119
  "done": None,
115
120
  "usage": "usage",
116
121
  "finish_reason": "choices[0].finish_reason",
117
122
  "extra_delta": {
118
- "tool_calls": "choices[0].delta.tool_calls",
119
123
  "function_call": "choices[0].delta.function_call",
120
124
  },
121
125
  "extra_done": None,
@@ -181,13 +185,25 @@ class OpenAICompatible(ModelRequester):
181
185
  request_data = {"prompt": self.prompt.to_text()}
182
186
  case "embeddings":
183
187
  sanitized_input = DataFormatter.sanitize(self.prompt["input"])
184
- request_data = {
185
- "input": (
186
- str(sanitized_input)
187
- if isinstance(sanitized_input, (str, int, float, bool)) or sanitized_input is None
188
- else yaml.safe_dump(sanitized_input)
189
- )
190
- }
188
+ if isinstance(sanitized_input, list):
189
+ request_data = {
190
+ "input": [
191
+ (
192
+ str(item)
193
+ if isinstance(item, (str, int, float, bool)) or item is None
194
+ else yaml.safe_dump(item)
195
+ )
196
+ for item in sanitized_input
197
+ ],
198
+ }
199
+ else:
200
+ request_data = {
201
+ "input": (
202
+ str(sanitized_input)
203
+ if isinstance(sanitized_input, (str, int, float, bool)) or sanitized_input is None
204
+ else yaml.safe_dump(sanitized_input)
205
+ )
206
+ }
191
207
  case _:
192
208
  self._messenger.error(
193
209
  TypeError(
@@ -200,17 +216,17 @@ class OpenAICompatible(ModelRequester):
200
216
  agently_request_dict["data"] = request_data
201
217
 
202
218
  # headers
203
- headers = DataFormatter.to_str_key_dict(
219
+ headers: dict[str, str] = DataFormatter.to_str_key_dict(
204
220
  self.plugin_settings.get("headers"),
205
221
  value_format="str",
206
- default={},
222
+ default_value={},
207
223
  )
208
224
  headers.update({"Connection": "close"})
209
225
  ## set
210
226
  agently_request_dict["headers"] = headers
211
227
 
212
228
  # client options
213
- client_options = DataFormatter.to_str_key_dict(self.plugin_settings.get("client_options"))
229
+ client_options = DataFormatter.to_str_key_dict(self.plugin_settings.get("client_options"), default_value={})
214
230
  ## proxy
215
231
  proxy = self.plugin_settings.get("proxy", None)
216
232
  if proxy:
@@ -225,7 +241,8 @@ class OpenAICompatible(ModelRequester):
225
241
  "write": 30.0,
226
242
  "pool": 30.0,
227
243
  },
228
- )
244
+ ),
245
+ default_value={},
229
246
  )
230
247
  timeout = Timeout(**timeout_configs)
231
248
  client_options.update({"timeout": timeout})
@@ -236,7 +253,16 @@ class OpenAICompatible(ModelRequester):
236
253
  request_options = DataFormatter.to_str_key_dict(
237
254
  self.plugin_settings.get("request_options"),
238
255
  value_format="serializable",
256
+ default_value={},
239
257
  )
258
+ request_options_in_prompt = self.prompt.get("options", {})
259
+ if request_options_in_prompt:
260
+ request_options.update(request_options_in_prompt)
261
+ request_options = DataFormatter.to_str_key_dict(
262
+ request_options,
263
+ value_format="serializable",
264
+ default_value={},
265
+ )
240
266
  ## !: ensure model
241
267
  request_options.update(
242
268
  {
@@ -245,18 +271,25 @@ class OpenAICompatible(ModelRequester):
245
271
  DataFormatter.to_str_key_dict(
246
272
  self.plugin_settings.get("default_model"),
247
273
  value_format="serializable",
248
- default={self.model_type: self.plugin_settings.get("default_model")},
274
+ default_key=self.model_type,
249
275
  )[self.model_type],
250
276
  )
251
277
  }
252
278
  )
253
279
  ## !: ensure stream
254
- is_stream = self.plugin_settings.get("stream", True)
280
+ is_stream = self.plugin_settings.get("stream")
281
+ if is_stream is None:
282
+ if self.model_type == "embeddings":
283
+ is_stream = False
284
+ else:
285
+ is_stream = True
255
286
  request_options.update({"stream": is_stream})
256
287
  ## set
257
288
  agently_request_dict["request_options"] = request_options
258
289
 
259
290
  # request url
291
+ ## get full url
292
+ full_url = self.plugin_settings.get("full_url")
260
293
  ## get base url
261
294
  base_url = str(self.plugin_settings.get("base_url"))
262
295
  base_url = base_url[:-1] if base_url[-1] == "/" else base_url
@@ -264,10 +297,14 @@ class OpenAICompatible(ModelRequester):
264
297
  path_mapping = DataFormatter.to_str_key_dict(
265
298
  self.plugin_settings.get("path_mapping"),
266
299
  value_format="str",
300
+ default_value={},
267
301
  )
268
302
  path_mapping = {k: v if v[0] == "/" else f"/{ v }" for k, v in path_mapping.items()}
269
303
  ## set
270
- request_url = f"{ base_url }{ path_mapping[self.model_type] }"
304
+ if isinstance(full_url, str):
305
+ request_url = full_url
306
+ else:
307
+ request_url = f"{ base_url }{ path_mapping[self.model_type] }"
271
308
  agently_request_dict["request_url"] = request_url
272
309
 
273
310
  return AgentlyRequestData(**agently_request_dict)
@@ -309,44 +346,82 @@ class OpenAICompatible(ModelRequester):
309
346
  auth = DataFormatter.to_str_key_dict(
310
347
  self.plugin_settings.get("auth", "None"),
311
348
  value_format="serializable",
312
- default={"api_key": self.plugin_settings.get("auth", "None")},
349
+ default_key="api_key",
313
350
  )
314
- headers_with_auth = {**request_data.headers, "Authorization": f"Bearer { auth['api_key'] }"}
351
+ api_key = self.plugin_settings.get("api_key", None)
352
+ if api_key is not None and auth["api_key"] == "None":
353
+ auth["api_key"] = str(api_key)
354
+ if "api_key" in auth:
355
+ headers_with_auth = {**request_data.headers, "Authorization": f"Bearer { auth['api_key'] }"}
356
+ elif "headers" in auth and isinstance(auth["headers"], dict):
357
+ headers_with_auth = {**request_data.headers, **auth["headers"]}
358
+ elif "body" in auth and isinstance(auth["body"], dict):
359
+ headers_with_auth = request_data.headers.copy()
360
+ request_data.data.update(**auth["body"])
361
+ else:
362
+ headers_with_auth = request_data.headers.copy()
315
363
 
316
364
  # request
317
365
  # stream request
318
366
  if self.model_type in ("chat", "completions") and request_data.stream:
319
367
  async with AsyncClient(**request_data.client_options) as client:
320
368
  client.headers.update(headers_with_auth)
321
- full_request_data = DataFormatter.to_str_key_dict(request_data.data, value_format="serializable")
369
+ full_request_data = DataFormatter.to_str_key_dict(
370
+ request_data.data,
371
+ value_format="serializable",
372
+ default_value={},
373
+ )
322
374
  full_request_data.update(request_data.request_options)
323
375
  try:
376
+ has_done = False
324
377
  async for sse in await self._aiter_sse_with_retry(
325
378
  client, "POST", request_data.request_url, json=full_request_data, headers=headers_with_auth
326
379
  ):
327
380
  yield sse.event, sse.data
381
+ if sse.data.strip() == "[DONE]":
382
+ has_done = False
383
+ if not has_done:
384
+ yield "message", "[DONE]"
328
385
  except SSEError as e:
329
386
  response = await client.post(
330
387
  request_data.request_url,
331
388
  json=full_request_data,
332
389
  headers=headers_with_auth,
333
390
  )
334
- content_type = response.headers.get("Content-Type", "")
335
- if content_type.startswith("application/json"):
336
- try:
337
- error_json = response.json()
338
- except Exception:
339
- error_json = await response.aread()
340
- error_json = json.loads(error_json.decode())
341
- error = error_json["error"]
342
- error_title = f"{ error['code'] if 'code' in error else 'unknown_code' } - { error['type'] if 'type' in error else 'unknown_type' }"
343
- error_detail = error["message"] if "message" in error else ""
344
- yield "error", error_detail
345
- else:
391
+ # Raise status code >= 400
392
+ if response.status_code >= 400:
393
+ e = RequestError(
394
+ f"Status Code: { response.status_code }\n" f"Request Data: {full_request_data}"
395
+ )
346
396
  self._messenger.error(
347
- "Error: SSE Error\n" f"Detail: {e}\n" f"Request Data: {full_request_data}", status="FAILED"
397
+ e,
398
+ status="FAILED",
348
399
  )
349
400
  yield "error", e
401
+ else:
402
+ content_type = response.headers.get("Content-Type", "")
403
+ if content_type.startswith("application/json"):
404
+ try:
405
+ error_json = response.json()
406
+ except Exception:
407
+ error_json = await response.aread()
408
+ error_json = json.loads(error_json.decode())
409
+ error = error_json["error"]
410
+ error_title = f"{ error['code'] if 'code' in error else 'unknown_code' } - { error['type'] if 'type' in error else 'unknown_type' }"
411
+ error_detail = error["message"] if "message" in error else ""
412
+ self._messenger.error(
413
+ f"Error: { error_title }\n"
414
+ f"Detail: {error_detail }\n"
415
+ f"Request Data: {full_request_data}",
416
+ status="FAILED",
417
+ )
418
+ yield "error", error_detail
419
+ else:
420
+ self._messenger.error(
421
+ "Error: SSE Error\n" f"Detail: {e}\n" f"Request Data: {full_request_data}",
422
+ status="FAILED",
423
+ )
424
+ yield "error", e
350
425
  except HTTPStatusError as e:
351
426
  self._messenger.error(
352
427
  "Error: HTTP Status Error\n"
@@ -373,15 +448,29 @@ class OpenAICompatible(ModelRequester):
373
448
  else:
374
449
  async with AsyncClient(**request_data.client_options) as client:
375
450
  client.headers.update(headers_with_auth)
376
- full_request_data = DataFormatter.to_str_key_dict(request_data.data, value_format="serializable")
451
+ full_request_data = DataFormatter.to_str_key_dict(
452
+ request_data.data,
453
+ value_format="serializable",
454
+ default_value={},
455
+ )
377
456
  full_request_data.update(request_data.request_options)
378
457
  try:
379
458
  response = await client.post(
380
459
  request_data.request_url,
381
460
  json=full_request_data,
382
461
  )
383
- yield "message", response.content.decode()
384
- yield "message", "[DONE]"
462
+ if response.status_code >= 400:
463
+ e = RequestError(
464
+ f"Status Code: { response.status_code }\n" f"Request Data: {full_request_data}"
465
+ )
466
+ self._messenger.error(
467
+ e,
468
+ status="FAILED",
469
+ )
470
+ yield "error", e
471
+ else:
472
+ yield "message", response.content.decode()
473
+ yield "message", "[DONE]"
385
474
  except HTTPStatusError as e:
386
475
  self._messenger.error(
387
476
  "Error: HTTP Status Error\n"
@@ -420,6 +509,7 @@ class OpenAICompatible(ModelRequester):
420
509
  id_mapping = content_mapping["id"]
421
510
  role_mapping = content_mapping["role"]
422
511
  delta_mapping = content_mapping["delta"]
512
+ tool_calls_mapping = content_mapping["tool_calls"]
423
513
  done_mapping = content_mapping["done"]
424
514
  usage_mapping = content_mapping["usage"]
425
515
  finish_reason_mapping = content_mapping["finish_reason"]
@@ -450,6 +540,7 @@ class OpenAICompatible(ModelRequester):
450
540
  loaded_message,
451
541
  role_mapping,
452
542
  style=content_mapping_style,
543
+ default="assistant",
453
544
  )
454
545
  if role:
455
546
  meta.update({"role": role})
@@ -462,6 +553,14 @@ class OpenAICompatible(ModelRequester):
462
553
  if delta:
463
554
  content_buffer += str(delta)
464
555
  yield "delta", delta
556
+ if tool_calls_mapping:
557
+ tool_calls = DataLocator.locate_path_in_dict(
558
+ loaded_message,
559
+ tool_calls_mapping,
560
+ style=content_mapping_style,
561
+ )
562
+ if tool_calls:
563
+ yield "tool_calls", tool_calls
465
564
  if extra_delta_mapping:
466
565
  for extra_key, extra_path in extra_delta_mapping.items():
467
566
  extra_value = DataLocator.locate_path_in_dict(
@@ -473,6 +572,9 @@ class OpenAICompatible(ModelRequester):
473
572
  yield "extra", {extra_key: extra_value}
474
573
  else:
475
574
  done_content = None
575
+ if self.model_type == "embeddings" and done_mapping is None:
576
+ done_mapping = "data"
577
+ content_mapping_style = "dot"
476
578
  if done_mapping:
477
579
  done_content = DataLocator.locate_path_in_dict(
478
580
  message_record,
@@ -483,16 +585,20 @@ class OpenAICompatible(ModelRequester):
483
585
  yield "done", done_content
484
586
  else:
485
587
  yield "done", content_buffer
486
- done_message = message_record
487
- if "message" not in done_message["choices"][0]:
488
- done_message["choices"][0].update({"message": {}})
489
- done_message["choices"][0]["message"].update(
490
- {
491
- "role": meta["role"] if "role" in meta else "assistant",
492
- "content": done_content if done_content else content_buffer,
493
- }
494
- )
495
- yield "original_done", done_message
588
+ match self.model_type:
589
+ case "embeddings":
590
+ yield "original_done", message_record
591
+ case "_":
592
+ done_message = message_record
593
+ if "message" not in done_message["choices"][0]:
594
+ done_message["choices"][0].update({"message": {}})
595
+ done_message["choices"][0]["message"].update(
596
+ {
597
+ "role": meta["role"] if "role" in meta else "assistant",
598
+ "content": done_content if done_content else content_buffer,
599
+ }
600
+ )
601
+ yield "original_done", done_message
496
602
  if finish_reason_mapping:
497
603
  meta.update(
498
604
  {