letta-nightly 0.1.7.dev20240924104148__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.

Potentially problematic release.


This version of letta-nightly might be problematic. Click here for more details.

Files changed (189) hide show
  1. letta/__init__.py +24 -0
  2. letta/__main__.py +3 -0
  3. letta/agent.py +1427 -0
  4. letta/agent_store/chroma.py +295 -0
  5. letta/agent_store/db.py +546 -0
  6. letta/agent_store/lancedb.py +177 -0
  7. letta/agent_store/milvus.py +198 -0
  8. letta/agent_store/qdrant.py +201 -0
  9. letta/agent_store/storage.py +188 -0
  10. letta/benchmark/benchmark.py +96 -0
  11. letta/benchmark/constants.py +14 -0
  12. letta/cli/cli.py +689 -0
  13. letta/cli/cli_config.py +1282 -0
  14. letta/cli/cli_load.py +166 -0
  15. letta/client/__init__.py +0 -0
  16. letta/client/admin.py +171 -0
  17. letta/client/client.py +2360 -0
  18. letta/client/streaming.py +90 -0
  19. letta/client/utils.py +61 -0
  20. letta/config.py +484 -0
  21. letta/configs/anthropic.json +13 -0
  22. letta/configs/letta_hosted.json +11 -0
  23. letta/configs/openai.json +12 -0
  24. letta/constants.py +134 -0
  25. letta/credentials.py +140 -0
  26. letta/data_sources/connectors.py +247 -0
  27. letta/embeddings.py +218 -0
  28. letta/errors.py +26 -0
  29. letta/functions/__init__.py +0 -0
  30. letta/functions/function_sets/base.py +174 -0
  31. letta/functions/function_sets/extras.py +132 -0
  32. letta/functions/functions.py +105 -0
  33. letta/functions/schema_generator.py +205 -0
  34. letta/humans/__init__.py +0 -0
  35. letta/humans/examples/basic.txt +1 -0
  36. letta/humans/examples/cs_phd.txt +9 -0
  37. letta/interface.py +314 -0
  38. letta/llm_api/__init__.py +0 -0
  39. letta/llm_api/anthropic.py +383 -0
  40. letta/llm_api/azure_openai.py +155 -0
  41. letta/llm_api/cohere.py +396 -0
  42. letta/llm_api/google_ai.py +468 -0
  43. letta/llm_api/llm_api_tools.py +485 -0
  44. letta/llm_api/openai.py +470 -0
  45. letta/local_llm/README.md +3 -0
  46. letta/local_llm/__init__.py +0 -0
  47. letta/local_llm/chat_completion_proxy.py +279 -0
  48. letta/local_llm/constants.py +31 -0
  49. letta/local_llm/function_parser.py +68 -0
  50. letta/local_llm/grammars/__init__.py +0 -0
  51. letta/local_llm/grammars/gbnf_grammar_generator.py +1324 -0
  52. letta/local_llm/grammars/json.gbnf +26 -0
  53. letta/local_llm/grammars/json_func_calls_with_inner_thoughts.gbnf +32 -0
  54. letta/local_llm/groq/api.py +97 -0
  55. letta/local_llm/json_parser.py +202 -0
  56. letta/local_llm/koboldcpp/api.py +62 -0
  57. letta/local_llm/koboldcpp/settings.py +23 -0
  58. letta/local_llm/llamacpp/api.py +58 -0
  59. letta/local_llm/llamacpp/settings.py +22 -0
  60. letta/local_llm/llm_chat_completion_wrappers/__init__.py +0 -0
  61. letta/local_llm/llm_chat_completion_wrappers/airoboros.py +452 -0
  62. letta/local_llm/llm_chat_completion_wrappers/chatml.py +470 -0
  63. letta/local_llm/llm_chat_completion_wrappers/configurable_wrapper.py +387 -0
  64. letta/local_llm/llm_chat_completion_wrappers/dolphin.py +246 -0
  65. letta/local_llm/llm_chat_completion_wrappers/llama3.py +345 -0
  66. letta/local_llm/llm_chat_completion_wrappers/simple_summary_wrapper.py +156 -0
  67. letta/local_llm/llm_chat_completion_wrappers/wrapper_base.py +11 -0
  68. letta/local_llm/llm_chat_completion_wrappers/zephyr.py +345 -0
  69. letta/local_llm/lmstudio/api.py +100 -0
  70. letta/local_llm/lmstudio/settings.py +29 -0
  71. letta/local_llm/ollama/api.py +88 -0
  72. letta/local_llm/ollama/settings.py +32 -0
  73. letta/local_llm/settings/__init__.py +0 -0
  74. letta/local_llm/settings/deterministic_mirostat.py +45 -0
  75. letta/local_llm/settings/settings.py +72 -0
  76. letta/local_llm/settings/simple.py +28 -0
  77. letta/local_llm/utils.py +265 -0
  78. letta/local_llm/vllm/api.py +63 -0
  79. letta/local_llm/webui/api.py +60 -0
  80. letta/local_llm/webui/legacy_api.py +58 -0
  81. letta/local_llm/webui/legacy_settings.py +23 -0
  82. letta/local_llm/webui/settings.py +24 -0
  83. letta/log.py +76 -0
  84. letta/main.py +437 -0
  85. letta/memory.py +440 -0
  86. letta/metadata.py +884 -0
  87. letta/openai_backcompat/__init__.py +0 -0
  88. letta/openai_backcompat/openai_object.py +437 -0
  89. letta/persistence_manager.py +148 -0
  90. letta/personas/__init__.py +0 -0
  91. letta/personas/examples/anna_pa.txt +13 -0
  92. letta/personas/examples/google_search_persona.txt +15 -0
  93. letta/personas/examples/memgpt_doc.txt +6 -0
  94. letta/personas/examples/memgpt_starter.txt +4 -0
  95. letta/personas/examples/sam.txt +14 -0
  96. letta/personas/examples/sam_pov.txt +14 -0
  97. letta/personas/examples/sam_simple_pov_gpt35.txt +13 -0
  98. letta/personas/examples/sqldb/test.db +0 -0
  99. letta/prompts/__init__.py +0 -0
  100. letta/prompts/gpt_summarize.py +14 -0
  101. letta/prompts/gpt_system.py +26 -0
  102. letta/prompts/system/memgpt_base.txt +49 -0
  103. letta/prompts/system/memgpt_chat.txt +58 -0
  104. letta/prompts/system/memgpt_chat_compressed.txt +13 -0
  105. letta/prompts/system/memgpt_chat_fstring.txt +51 -0
  106. letta/prompts/system/memgpt_doc.txt +50 -0
  107. letta/prompts/system/memgpt_gpt35_extralong.txt +53 -0
  108. letta/prompts/system/memgpt_intuitive_knowledge.txt +31 -0
  109. letta/prompts/system/memgpt_modified_chat.txt +23 -0
  110. letta/pytest.ini +0 -0
  111. letta/schemas/agent.py +117 -0
  112. letta/schemas/api_key.py +21 -0
  113. letta/schemas/block.py +135 -0
  114. letta/schemas/document.py +21 -0
  115. letta/schemas/embedding_config.py +54 -0
  116. letta/schemas/enums.py +35 -0
  117. letta/schemas/job.py +38 -0
  118. letta/schemas/letta_base.py +80 -0
  119. letta/schemas/letta_message.py +175 -0
  120. letta/schemas/letta_request.py +23 -0
  121. letta/schemas/letta_response.py +28 -0
  122. letta/schemas/llm_config.py +54 -0
  123. letta/schemas/memory.py +224 -0
  124. letta/schemas/message.py +727 -0
  125. letta/schemas/openai/chat_completion_request.py +123 -0
  126. letta/schemas/openai/chat_completion_response.py +136 -0
  127. letta/schemas/openai/chat_completions.py +123 -0
  128. letta/schemas/openai/embedding_response.py +11 -0
  129. letta/schemas/openai/openai.py +157 -0
  130. letta/schemas/organization.py +20 -0
  131. letta/schemas/passage.py +80 -0
  132. letta/schemas/source.py +62 -0
  133. letta/schemas/tool.py +143 -0
  134. letta/schemas/usage.py +18 -0
  135. letta/schemas/user.py +33 -0
  136. letta/server/__init__.py +0 -0
  137. letta/server/constants.py +6 -0
  138. letta/server/rest_api/__init__.py +0 -0
  139. letta/server/rest_api/admin/__init__.py +0 -0
  140. letta/server/rest_api/admin/agents.py +21 -0
  141. letta/server/rest_api/admin/tools.py +83 -0
  142. letta/server/rest_api/admin/users.py +98 -0
  143. letta/server/rest_api/app.py +193 -0
  144. letta/server/rest_api/auth/__init__.py +0 -0
  145. letta/server/rest_api/auth/index.py +43 -0
  146. letta/server/rest_api/auth_token.py +22 -0
  147. letta/server/rest_api/interface.py +726 -0
  148. letta/server/rest_api/routers/__init__.py +0 -0
  149. letta/server/rest_api/routers/openai/__init__.py +0 -0
  150. letta/server/rest_api/routers/openai/assistants/__init__.py +0 -0
  151. letta/server/rest_api/routers/openai/assistants/assistants.py +115 -0
  152. letta/server/rest_api/routers/openai/assistants/schemas.py +121 -0
  153. letta/server/rest_api/routers/openai/assistants/threads.py +336 -0
  154. letta/server/rest_api/routers/openai/chat_completions/__init__.py +0 -0
  155. letta/server/rest_api/routers/openai/chat_completions/chat_completions.py +131 -0
  156. letta/server/rest_api/routers/v1/__init__.py +15 -0
  157. letta/server/rest_api/routers/v1/agents.py +543 -0
  158. letta/server/rest_api/routers/v1/blocks.py +73 -0
  159. letta/server/rest_api/routers/v1/jobs.py +46 -0
  160. letta/server/rest_api/routers/v1/llms.py +28 -0
  161. letta/server/rest_api/routers/v1/organizations.py +61 -0
  162. letta/server/rest_api/routers/v1/sources.py +199 -0
  163. letta/server/rest_api/routers/v1/tools.py +103 -0
  164. letta/server/rest_api/routers/v1/users.py +109 -0
  165. letta/server/rest_api/static_files.py +74 -0
  166. letta/server/rest_api/utils.py +69 -0
  167. letta/server/server.py +1995 -0
  168. letta/server/startup.sh +8 -0
  169. letta/server/static_files/assets/index-0cbf7ad5.js +274 -0
  170. letta/server/static_files/assets/index-156816da.css +1 -0
  171. letta/server/static_files/assets/index-486e3228.js +274 -0
  172. letta/server/static_files/favicon.ico +0 -0
  173. letta/server/static_files/index.html +39 -0
  174. letta/server/static_files/memgpt_logo_transparent.png +0 -0
  175. letta/server/utils.py +46 -0
  176. letta/server/ws_api/__init__.py +0 -0
  177. letta/server/ws_api/example_client.py +104 -0
  178. letta/server/ws_api/interface.py +108 -0
  179. letta/server/ws_api/protocol.py +100 -0
  180. letta/server/ws_api/server.py +145 -0
  181. letta/settings.py +165 -0
  182. letta/streaming_interface.py +396 -0
  183. letta/system.py +207 -0
  184. letta/utils.py +1065 -0
  185. letta_nightly-0.1.7.dev20240924104148.dist-info/LICENSE +190 -0
  186. letta_nightly-0.1.7.dev20240924104148.dist-info/METADATA +98 -0
  187. letta_nightly-0.1.7.dev20240924104148.dist-info/RECORD +189 -0
  188. letta_nightly-0.1.7.dev20240924104148.dist-info/WHEEL +4 -0
  189. letta_nightly-0.1.7.dev20240924104148.dist-info/entry_points.txt +3 -0
@@ -0,0 +1,396 @@
1
+ import json
2
+ import uuid
3
+ from typing import List, Optional, Union
4
+
5
+ import requests
6
+
7
+ from letta.local_llm.utils import count_tokens
8
+ from letta.schemas.message import Message
9
+ from letta.schemas.openai.chat_completion_request import ChatCompletionRequest, Tool
10
+ from letta.schemas.openai.chat_completion_response import (
11
+ ChatCompletionResponse,
12
+ Choice,
13
+ FunctionCall,
14
+ )
15
+ from letta.schemas.openai.chat_completion_response import (
16
+ Message as ChoiceMessage, # NOTE: avoid conflict with our own Letta Message datatype
17
+ )
18
+ from letta.schemas.openai.chat_completion_response import ToolCall, UsageStatistics
19
+ from letta.utils import get_tool_call_id, get_utc_time, json_dumps, smart_urljoin
20
+
21
+ BASE_URL = "https://api.cohere.ai/v1"
22
+
23
+ # models that we know will work with Letta
24
+ COHERE_VALID_MODEL_LIST = [
25
+ "command-r-plus",
26
+ ]
27
+
28
+
29
+ def cohere_get_model_details(url: str, api_key: Union[str, None], model: str) -> int:
30
+ """https://docs.cohere.com/reference/get-model"""
31
+ from letta.utils import printd
32
+
33
+ url = smart_urljoin(url, "models")
34
+ url = smart_urljoin(url, model)
35
+ headers = {
36
+ "accept": "application/json",
37
+ "authorization": f"bearer {api_key}",
38
+ }
39
+
40
+ printd(f"Sending request to {url}")
41
+ try:
42
+ response = requests.get(url, headers=headers)
43
+ printd(f"response = {response}")
44
+ response.raise_for_status() # Raises HTTPError for 4XX/5XX status
45
+ response = response.json() # convert to dict from string
46
+ return response
47
+ except requests.exceptions.HTTPError as http_err:
48
+ # Handle HTTP errors (e.g., response 4XX, 5XX)
49
+ printd(f"Got HTTPError, exception={http_err}")
50
+ raise http_err
51
+ except requests.exceptions.RequestException as req_err:
52
+ # Handle other requests-related errors (e.g., connection error)
53
+ printd(f"Got RequestException, exception={req_err}")
54
+ raise req_err
55
+ except Exception as e:
56
+ # Handle other potential errors
57
+ printd(f"Got unknown Exception, exception={e}")
58
+ raise e
59
+
60
+
61
+ def cohere_get_model_context_window(url: str, api_key: Union[str, None], model: str) -> int:
62
+ model_details = cohere_get_model_details(url=url, api_key=api_key, model=model)
63
+ return model_details["context_length"]
64
+
65
+
66
+ def cohere_get_model_list(url: str, api_key: Union[str, None]) -> dict:
67
+ """https://docs.cohere.com/reference/list-models"""
68
+ from letta.utils import printd
69
+
70
+ url = smart_urljoin(url, "models")
71
+ headers = {
72
+ "accept": "application/json",
73
+ "authorization": f"bearer {api_key}",
74
+ }
75
+
76
+ printd(f"Sending request to {url}")
77
+ try:
78
+ response = requests.get(url, headers=headers)
79
+ printd(f"response = {response}")
80
+ response.raise_for_status() # Raises HTTPError for 4XX/5XX status
81
+ response = response.json() # convert to dict from string
82
+ return response["models"]
83
+ except requests.exceptions.HTTPError as http_err:
84
+ # Handle HTTP errors (e.g., response 4XX, 5XX)
85
+ printd(f"Got HTTPError, exception={http_err}")
86
+ raise http_err
87
+ except requests.exceptions.RequestException as req_err:
88
+ # Handle other requests-related errors (e.g., connection error)
89
+ printd(f"Got RequestException, exception={req_err}")
90
+ raise req_err
91
+ except Exception as e:
92
+ # Handle other potential errors
93
+ printd(f"Got unknown Exception, exception={e}")
94
+ raise e
95
+
96
+
97
+ def remap_finish_reason(finish_reason: str) -> str:
98
+ """Remap Cohere's 'finish_reason' to OpenAI 'finish_reason'
99
+
100
+ OpenAI: 'stop', 'length', 'function_call', 'content_filter', null
101
+ see: https://platform.openai.com/docs/guides/text-generation/chat-completions-api
102
+
103
+ Cohere finish_reason is different but undocumented ???
104
+ """
105
+ if finish_reason == "COMPLETE":
106
+ return "stop"
107
+ elif finish_reason == "MAX_TOKENS":
108
+ return "length"
109
+ # elif stop_reason == "tool_use":
110
+ # return "function_call"
111
+ else:
112
+ raise ValueError(f"Unexpected stop_reason: {finish_reason}")
113
+
114
+
115
+ def convert_cohere_response_to_chatcompletion(
116
+ response_json: dict, # REST response from API
117
+ model: str, # Required since not returned
118
+ inner_thoughts_in_kwargs: Optional[bool] = True,
119
+ ) -> ChatCompletionResponse:
120
+ """
121
+ Example response from command-r-plus:
122
+ response.json = {
123
+ 'response_id': '28c47751-acce-41cd-8c89-c48a15ac33cf',
124
+ 'text': '',
125
+ 'generation_id': '84209c9e-2868-4984-82c5-063b748b7776',
126
+ 'chat_history': [
127
+ {
128
+ 'role': 'CHATBOT',
129
+ 'message': 'Bootup sequence complete. Persona activated. Testing messaging functionality.'
130
+ },
131
+ {
132
+ 'role': 'SYSTEM',
133
+ 'message': '{"status": "OK", "message": null, "time": "2024-04-11 11:22:36 PM PDT-0700"}'
134
+ }
135
+ ],
136
+ 'finish_reason': 'COMPLETE',
137
+ 'meta': {
138
+ 'api_version': {'version': '1'},
139
+ 'billed_units': {'input_tokens': 692, 'output_tokens': 20},
140
+ 'tokens': {'output_tokens': 20}
141
+ },
142
+ 'tool_calls': [
143
+ {
144
+ 'name': 'send_message',
145
+ 'parameters': {
146
+ 'message': "Hello Chad, it's Sam. How are you feeling today?"
147
+ }
148
+ }
149
+ ]
150
+ }
151
+ """
152
+ if "billed_units" in response_json["meta"]:
153
+ prompt_tokens = response_json["meta"]["billed_units"]["input_tokens"]
154
+ completion_tokens = response_json["meta"]["billed_units"]["output_tokens"]
155
+ else:
156
+ # For some reason input_tokens not included in 'meta' 'tokens' dict?
157
+ prompt_tokens = count_tokens(json_dumps(response_json["chat_history"])) # NOTE: this is a very rough approximation
158
+ completion_tokens = response_json["meta"]["tokens"]["output_tokens"]
159
+
160
+ finish_reason = remap_finish_reason(response_json["finish_reason"])
161
+
162
+ if "tool_calls" in response_json and response_json["tool_calls"] is not None:
163
+ inner_thoughts = []
164
+ tool_calls = []
165
+ for tool_call_response in response_json["tool_calls"]:
166
+ function_name = tool_call_response["name"]
167
+ function_args = tool_call_response["parameters"]
168
+ if inner_thoughts_in_kwargs:
169
+ from letta.local_llm.constants import INNER_THOUGHTS_KWARG
170
+
171
+ assert INNER_THOUGHTS_KWARG in function_args
172
+ # NOTE:
173
+ inner_thoughts.append(function_args.pop(INNER_THOUGHTS_KWARG))
174
+
175
+ tool_calls.append(
176
+ ToolCall(
177
+ id=get_tool_call_id(),
178
+ type="function",
179
+ function=FunctionCall(
180
+ name=function_name,
181
+ arguments=json.dumps(function_args),
182
+ ),
183
+ )
184
+ )
185
+
186
+ # NOTE: no multi-call support for now
187
+ assert len(tool_calls) == 1, tool_calls
188
+ content = inner_thoughts[0]
189
+
190
+ else:
191
+ # raise NotImplementedError(f"Expected a tool call response from Cohere API")
192
+ content = response_json["text"]
193
+ tool_calls = None
194
+
195
+ # In Cohere API empty string == null
196
+ content = None if content == "" else content
197
+ assert content is not None or tool_calls is not None, "Response message must have either content or tool_calls"
198
+
199
+ choice = Choice(
200
+ index=0,
201
+ finish_reason=finish_reason,
202
+ message=ChoiceMessage(
203
+ role="assistant",
204
+ content=content,
205
+ tool_calls=tool_calls,
206
+ ),
207
+ )
208
+
209
+ return ChatCompletionResponse(
210
+ id=response_json["response_id"],
211
+ choices=[choice],
212
+ created=get_utc_time(),
213
+ model=model,
214
+ usage=UsageStatistics(
215
+ prompt_tokens=prompt_tokens,
216
+ completion_tokens=completion_tokens,
217
+ total_tokens=prompt_tokens + completion_tokens,
218
+ ),
219
+ )
220
+
221
+
222
+ def convert_tools_to_cohere_format(tools: List[Tool], inner_thoughts_in_kwargs: Optional[bool] = True) -> List[dict]:
223
+ """See: https://docs.cohere.com/reference/chat
224
+
225
+ OpenAI style:
226
+ "tools": [{
227
+ "type": "function",
228
+ "function": {
229
+ "name": "find_movies",
230
+ "description": "find ....",
231
+ "parameters": {
232
+ "type": "object",
233
+ "properties": {
234
+ PARAM: {
235
+ "type": PARAM_TYPE, # eg "string"
236
+ "description": PARAM_DESCRIPTION,
237
+ },
238
+ ...
239
+ },
240
+ "required": List[str],
241
+ }
242
+ }
243
+ }]
244
+
245
+ Cohere style:
246
+ "tools": [{
247
+ "name": "find_movies",
248
+ "description": "find ....",
249
+ "parameter_definitions": {
250
+ PARAM_NAME: {
251
+ "description": PARAM_DESCRIPTION,
252
+ "type": PARAM_TYPE, # eg "string"
253
+ "required": <boolean>,
254
+ }
255
+ },
256
+ }
257
+ }]
258
+ """
259
+ tools_dict_list = []
260
+ for tool in tools:
261
+ tools_dict_list.append(
262
+ {
263
+ "name": tool.function.name,
264
+ "description": tool.function.description,
265
+ "parameter_definitions": {
266
+ p_name: {
267
+ "description": p_fields["description"],
268
+ "type": p_fields["type"],
269
+ "required": p_name in tool.function.parameters["required"],
270
+ }
271
+ for p_name, p_fields in tool.function.parameters["properties"].items()
272
+ },
273
+ }
274
+ )
275
+
276
+ if inner_thoughts_in_kwargs:
277
+ # NOTE: since Cohere doesn't allow "text" in the response when a tool call happens, if we want
278
+ # a simultaneous CoT + tool call we need to put it inside a kwarg
279
+ from letta.local_llm.constants import (
280
+ INNER_THOUGHTS_KWARG,
281
+ INNER_THOUGHTS_KWARG_DESCRIPTION,
282
+ )
283
+
284
+ for cohere_tool in tools_dict_list:
285
+ cohere_tool["parameter_definitions"][INNER_THOUGHTS_KWARG] = {
286
+ "description": INNER_THOUGHTS_KWARG_DESCRIPTION,
287
+ "type": "string",
288
+ "required": True,
289
+ }
290
+
291
+ return tools_dict_list
292
+
293
+
294
+ def cohere_chat_completions_request(
295
+ url: str,
296
+ api_key: str,
297
+ chat_completion_request: ChatCompletionRequest,
298
+ ) -> ChatCompletionResponse:
299
+ """https://docs.cohere.com/docs/multi-step-tool-use"""
300
+ from letta.utils import printd
301
+
302
+ url = smart_urljoin(url, "chat")
303
+ headers = {
304
+ "Content-Type": "application/json",
305
+ "Authorization": f"bearer {api_key}",
306
+ }
307
+
308
+ # convert the tools
309
+ cohere_tools = None if chat_completion_request.tools is None else convert_tools_to_cohere_format(chat_completion_request.tools)
310
+
311
+ # pydantic -> dict
312
+ data = chat_completion_request.model_dump(exclude_none=True)
313
+
314
+ if "functions" in data:
315
+ raise ValueError(f"'functions' unexpected in Anthropic API payload")
316
+
317
+ # If tools == None, strip from the payload
318
+ if "tools" in data and data["tools"] is None:
319
+ data.pop("tools")
320
+ data.pop("tool_choice", None) # extra safe, should exist always (default="auto")
321
+
322
+ # Convert messages to Cohere format
323
+ msg_objs = [Message.dict_to_message(user_id=uuid.uuid4(), agent_id=uuid.uuid4(), openai_message_dict=m) for m in data["messages"]]
324
+
325
+ # System message 0 should instead be a "preamble"
326
+ # See: https://docs.cohere.com/reference/chat
327
+ # The chat_history parameter should not be used for SYSTEM messages in most cases. Instead, to add a SYSTEM role message at the beginning of a conversation, the preamble parameter should be used.
328
+ assert msg_objs[0].role == "system", msg_objs[0]
329
+ preamble = msg_objs[0].text
330
+
331
+ # data["messages"] = [m.to_cohere_dict() for m in msg_objs[1:]]
332
+ data["messages"] = []
333
+ for m in msg_objs[1:]:
334
+ ms = m.to_cohere_dict() # NOTE: returns List[dict]
335
+ data["messages"].extend(ms)
336
+
337
+ assert data["messages"][-1]["role"] == "USER", data["messages"][-1]
338
+ data = {
339
+ "preamble": preamble,
340
+ "chat_history": data["messages"][:-1],
341
+ "message": data["messages"][-1]["message"],
342
+ "tools": cohere_tools,
343
+ }
344
+
345
+ # Move 'system' to the top level
346
+ # 'messages: Unexpected role "system". The Messages API accepts a top-level `system` parameter, not "system" as an input message role.'
347
+ # assert data["messages"][0]["role"] == "system", f"Expected 'system' role in messages[0]:\n{data['messages'][0]}"
348
+ # data["system"] = data["messages"][0]["content"]
349
+ # data["messages"] = data["messages"][1:]
350
+
351
+ # Convert to Anthropic format
352
+ # msg_objs = [Message.dict_to_message(user_id=uuid.uuid4(), agent_id=uuid.uuid4(), openai_message_dict=m) for m in data["messages"]]
353
+ # data["messages"] = [m.to_anthropic_dict(inner_thoughts_xml_tag=inner_thoughts_xml_tag) for m in msg_objs]
354
+
355
+ # Handling Anthropic special requirement for 'user' message in front
356
+ # messages: first message must use the "user" role'
357
+ # if data["messages"][0]["role"] != "user":
358
+ # data["messages"] = [{"role": "user", "content": DUMMY_FIRST_USER_MESSAGE}] + data["messages"]
359
+
360
+ # Handle Anthropic's restriction on alternating user/assistant messages
361
+ # data["messages"] = merge_tool_results_into_user_messages(data["messages"])
362
+
363
+ # Anthropic also wants max_tokens in the input
364
+ # It's also part of ChatCompletions
365
+ # assert "max_tokens" in data, data
366
+
367
+ # Remove extra fields used by OpenAI but not Anthropic
368
+ # data.pop("frequency_penalty", None)
369
+ # data.pop("logprobs", None)
370
+ # data.pop("n", None)
371
+ # data.pop("top_p", None)
372
+ # data.pop("presence_penalty", None)
373
+ # data.pop("user", None)
374
+ # data.pop("tool_choice", None)
375
+
376
+ printd(f"Sending request to {url}")
377
+ try:
378
+ response = requests.post(url, headers=headers, json=data)
379
+ printd(f"response = {response}")
380
+ response.raise_for_status() # Raises HTTPError for 4XX/5XX status
381
+ response = response.json() # convert to dict from string
382
+ printd(f"response.json = {response}")
383
+ response = convert_cohere_response_to_chatcompletion(response_json=response, model=chat_completion_request.model)
384
+ return response
385
+ except requests.exceptions.HTTPError as http_err:
386
+ # Handle HTTP errors (e.g., response 4XX, 5XX)
387
+ printd(f"Got HTTPError, exception={http_err}, payload={data}")
388
+ raise http_err
389
+ except requests.exceptions.RequestException as req_err:
390
+ # Handle other requests-related errors (e.g., connection error)
391
+ printd(f"Got RequestException, exception={req_err}")
392
+ raise req_err
393
+ except Exception as e:
394
+ # Handle other potential errors
395
+ printd(f"Got unknown Exception, exception={e}")
396
+ raise e