letta-nightly 0.12.1.dev20251024104217__py3-none-any.whl → 0.13.0.dev20251025104015__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.
- letta/__init__.py +2 -3
- letta/adapters/letta_llm_adapter.py +1 -0
- letta/adapters/simple_llm_request_adapter.py +8 -5
- letta/adapters/simple_llm_stream_adapter.py +22 -6
- letta/agents/agent_loop.py +10 -3
- letta/agents/base_agent.py +4 -1
- letta/agents/helpers.py +41 -9
- letta/agents/letta_agent.py +11 -10
- letta/agents/letta_agent_v2.py +47 -37
- letta/agents/letta_agent_v3.py +395 -300
- letta/agents/voice_agent.py +8 -6
- letta/agents/voice_sleeptime_agent.py +3 -3
- letta/constants.py +30 -7
- letta/errors.py +20 -0
- letta/functions/function_sets/base.py +55 -3
- letta/functions/mcp_client/types.py +33 -57
- letta/functions/schema_generator.py +135 -23
- letta/groups/sleeptime_multi_agent_v3.py +6 -11
- letta/groups/sleeptime_multi_agent_v4.py +227 -0
- letta/helpers/converters.py +78 -4
- letta/helpers/crypto_utils.py +6 -2
- letta/interfaces/anthropic_parallel_tool_call_streaming_interface.py +9 -11
- letta/interfaces/anthropic_streaming_interface.py +3 -4
- letta/interfaces/gemini_streaming_interface.py +4 -6
- letta/interfaces/openai_streaming_interface.py +63 -28
- letta/llm_api/anthropic_client.py +7 -4
- letta/llm_api/deepseek_client.py +6 -4
- letta/llm_api/google_ai_client.py +3 -12
- letta/llm_api/google_vertex_client.py +1 -1
- letta/llm_api/helpers.py +90 -61
- letta/llm_api/llm_api_tools.py +4 -1
- letta/llm_api/openai.py +12 -12
- letta/llm_api/openai_client.py +53 -16
- letta/local_llm/constants.py +4 -3
- letta/local_llm/json_parser.py +5 -2
- letta/local_llm/utils.py +2 -3
- letta/log.py +171 -7
- letta/orm/agent.py +43 -9
- letta/orm/archive.py +4 -0
- letta/orm/custom_columns.py +15 -0
- letta/orm/identity.py +11 -11
- letta/orm/mcp_server.py +9 -0
- letta/orm/message.py +6 -1
- letta/orm/run_metrics.py +7 -2
- letta/orm/sqlalchemy_base.py +2 -2
- letta/orm/tool.py +3 -0
- letta/otel/tracing.py +2 -0
- letta/prompts/prompt_generator.py +7 -2
- letta/schemas/agent.py +41 -10
- letta/schemas/agent_file.py +3 -0
- letta/schemas/archive.py +4 -2
- letta/schemas/block.py +2 -1
- letta/schemas/enums.py +36 -3
- letta/schemas/file.py +3 -3
- letta/schemas/folder.py +2 -1
- letta/schemas/group.py +2 -1
- letta/schemas/identity.py +18 -9
- letta/schemas/job.py +3 -1
- letta/schemas/letta_message.py +71 -12
- letta/schemas/letta_request.py +7 -3
- letta/schemas/letta_stop_reason.py +0 -25
- letta/schemas/llm_config.py +8 -2
- letta/schemas/mcp.py +80 -83
- letta/schemas/mcp_server.py +349 -0
- letta/schemas/memory.py +20 -8
- letta/schemas/message.py +212 -67
- letta/schemas/providers/anthropic.py +13 -6
- letta/schemas/providers/azure.py +6 -4
- letta/schemas/providers/base.py +8 -4
- letta/schemas/providers/bedrock.py +6 -2
- letta/schemas/providers/cerebras.py +7 -3
- letta/schemas/providers/deepseek.py +2 -1
- letta/schemas/providers/google_gemini.py +15 -6
- letta/schemas/providers/groq.py +2 -1
- letta/schemas/providers/lmstudio.py +9 -6
- letta/schemas/providers/mistral.py +2 -1
- letta/schemas/providers/openai.py +7 -2
- letta/schemas/providers/together.py +9 -3
- letta/schemas/providers/xai.py +7 -3
- letta/schemas/run.py +7 -2
- letta/schemas/run_metrics.py +2 -1
- letta/schemas/sandbox_config.py +2 -2
- letta/schemas/secret.py +3 -158
- letta/schemas/source.py +2 -2
- letta/schemas/step.py +2 -2
- letta/schemas/tool.py +24 -1
- letta/schemas/usage.py +0 -1
- letta/server/rest_api/app.py +123 -7
- letta/server/rest_api/dependencies.py +3 -0
- letta/server/rest_api/interface.py +7 -4
- letta/server/rest_api/redis_stream_manager.py +16 -1
- letta/server/rest_api/routers/v1/__init__.py +7 -0
- letta/server/rest_api/routers/v1/agents.py +332 -322
- letta/server/rest_api/routers/v1/archives.py +127 -40
- letta/server/rest_api/routers/v1/blocks.py +54 -6
- letta/server/rest_api/routers/v1/chat_completions.py +146 -0
- letta/server/rest_api/routers/v1/folders.py +27 -35
- letta/server/rest_api/routers/v1/groups.py +23 -35
- letta/server/rest_api/routers/v1/identities.py +24 -10
- letta/server/rest_api/routers/v1/internal_runs.py +107 -0
- letta/server/rest_api/routers/v1/internal_templates.py +162 -179
- letta/server/rest_api/routers/v1/jobs.py +15 -27
- letta/server/rest_api/routers/v1/mcp_servers.py +309 -0
- letta/server/rest_api/routers/v1/messages.py +23 -34
- letta/server/rest_api/routers/v1/organizations.py +6 -27
- letta/server/rest_api/routers/v1/providers.py +35 -62
- letta/server/rest_api/routers/v1/runs.py +30 -43
- letta/server/rest_api/routers/v1/sandbox_configs.py +6 -4
- letta/server/rest_api/routers/v1/sources.py +26 -42
- letta/server/rest_api/routers/v1/steps.py +16 -29
- letta/server/rest_api/routers/v1/tools.py +17 -13
- letta/server/rest_api/routers/v1/users.py +5 -17
- letta/server/rest_api/routers/v1/voice.py +18 -27
- letta/server/rest_api/streaming_response.py +5 -2
- letta/server/rest_api/utils.py +187 -25
- letta/server/server.py +27 -22
- letta/server/ws_api/server.py +5 -4
- letta/services/agent_manager.py +148 -26
- letta/services/agent_serialization_manager.py +6 -1
- letta/services/archive_manager.py +168 -15
- letta/services/block_manager.py +14 -4
- letta/services/file_manager.py +33 -29
- letta/services/group_manager.py +10 -0
- letta/services/helpers/agent_manager_helper.py +65 -11
- letta/services/identity_manager.py +105 -4
- letta/services/job_manager.py +11 -1
- letta/services/mcp/base_client.py +2 -2
- letta/services/mcp/oauth_utils.py +33 -8
- letta/services/mcp_manager.py +174 -78
- letta/services/mcp_server_manager.py +1331 -0
- letta/services/message_manager.py +109 -4
- letta/services/organization_manager.py +4 -4
- letta/services/passage_manager.py +9 -25
- letta/services/provider_manager.py +91 -15
- letta/services/run_manager.py +72 -15
- letta/services/sandbox_config_manager.py +45 -3
- letta/services/source_manager.py +15 -8
- letta/services/step_manager.py +24 -1
- letta/services/streaming_service.py +581 -0
- letta/services/summarizer/summarizer.py +1 -1
- letta/services/tool_executor/core_tool_executor.py +111 -0
- letta/services/tool_executor/files_tool_executor.py +5 -3
- letta/services/tool_executor/sandbox_tool_executor.py +2 -2
- letta/services/tool_executor/tool_execution_manager.py +1 -1
- letta/services/tool_manager.py +10 -3
- letta/services/tool_sandbox/base.py +61 -1
- letta/services/tool_sandbox/local_sandbox.py +1 -3
- letta/services/user_manager.py +2 -2
- letta/settings.py +49 -5
- letta/system.py +14 -5
- letta/utils.py +73 -1
- letta/validators.py +105 -0
- {letta_nightly-0.12.1.dev20251024104217.dist-info → letta_nightly-0.13.0.dev20251025104015.dist-info}/METADATA +4 -2
- {letta_nightly-0.12.1.dev20251024104217.dist-info → letta_nightly-0.13.0.dev20251025104015.dist-info}/RECORD +157 -151
- letta/schemas/letta_ping.py +0 -28
- letta/server/rest_api/routers/openai/chat_completions/__init__.py +0 -0
- {letta_nightly-0.12.1.dev20251024104217.dist-info → letta_nightly-0.13.0.dev20251025104015.dist-info}/WHEEL +0 -0
- {letta_nightly-0.12.1.dev20251024104217.dist-info → letta_nightly-0.13.0.dev20251025104015.dist-info}/entry_points.txt +0 -0
- {letta_nightly-0.12.1.dev20251024104217.dist-info → letta_nightly-0.13.0.dev20251025104015.dist-info}/licenses/LICENSE +0 -0
letta/server/rest_api/app.py
CHANGED
|
@@ -11,6 +11,7 @@ from typing import Optional
|
|
|
11
11
|
|
|
12
12
|
import uvicorn
|
|
13
13
|
from fastapi import FastAPI, Request
|
|
14
|
+
from fastapi.exceptions import RequestValidationError
|
|
14
15
|
from fastapi.responses import JSONResponse
|
|
15
16
|
from marshmallow import ValidationError
|
|
16
17
|
from sqlalchemy.exc import IntegrityError, OperationalError
|
|
@@ -26,29 +27,33 @@ from letta.errors import (
|
|
|
26
27
|
AgentNotFoundForExportError,
|
|
27
28
|
BedrockPermissionError,
|
|
28
29
|
LettaAgentNotFoundError,
|
|
30
|
+
LettaExpiredError,
|
|
29
31
|
LettaInvalidArgumentError,
|
|
30
32
|
LettaInvalidMCPSchemaError,
|
|
31
33
|
LettaMCPConnectionError,
|
|
32
34
|
LettaMCPTimeoutError,
|
|
35
|
+
LettaServiceUnavailableError,
|
|
33
36
|
LettaToolCreateError,
|
|
34
37
|
LettaToolNameConflictError,
|
|
38
|
+
LettaUnsupportedFileUploadError,
|
|
35
39
|
LettaUserNotFoundError,
|
|
36
40
|
LLMAuthenticationError,
|
|
37
41
|
LLMError,
|
|
38
42
|
LLMRateLimitError,
|
|
39
43
|
LLMTimeoutError,
|
|
44
|
+
PendingApprovalError,
|
|
40
45
|
)
|
|
41
46
|
from letta.helpers.pinecone_utils import get_pinecone_indices, should_use_pinecone, upsert_pinecone_indices
|
|
42
47
|
from letta.jobs.scheduler import start_scheduler_with_leader_election
|
|
43
48
|
from letta.log import get_logger
|
|
44
49
|
from letta.orm.errors import DatabaseTimeoutError, ForeignKeyConstraintViolationError, NoResultFound, UniqueConstraintViolationError
|
|
45
|
-
from letta.
|
|
50
|
+
from letta.otel.tracing import get_trace_id
|
|
51
|
+
from letta.schemas.letta_message import create_letta_message_union_schema, create_letta_ping_schema
|
|
46
52
|
from letta.schemas.letta_message_content import (
|
|
47
53
|
create_letta_assistant_message_content_union_schema,
|
|
48
54
|
create_letta_message_content_union_schema,
|
|
49
55
|
create_letta_user_message_content_union_schema,
|
|
50
56
|
)
|
|
51
|
-
from letta.schemas.letta_ping import create_letta_ping_schema
|
|
52
57
|
from letta.server.constants import REST_DEFAULT_PORT
|
|
53
58
|
from letta.server.db import db_registry
|
|
54
59
|
|
|
@@ -63,6 +68,7 @@ from letta.server.rest_api.static_files import mount_static_files
|
|
|
63
68
|
from letta.server.rest_api.utils import SENTRY_ENABLED
|
|
64
69
|
from letta.server.server import SyncServer
|
|
65
70
|
from letta.settings import settings, telemetry_settings
|
|
71
|
+
from letta.validators import PATH_VALIDATORS, PRIMITIVE_ID_PATTERNS
|
|
66
72
|
|
|
67
73
|
if SENTRY_ENABLED:
|
|
68
74
|
import sentry_sdk
|
|
@@ -197,7 +203,49 @@ def create_application() -> "FastAPI":
|
|
|
197
203
|
"continuous_profiling_auto_start": True,
|
|
198
204
|
},
|
|
199
205
|
)
|
|
200
|
-
|
|
206
|
+
|
|
207
|
+
if telemetry_settings.enable_datadog:
|
|
208
|
+
try:
|
|
209
|
+
dd_env = settings.environment or "development"
|
|
210
|
+
print(f"▶ Initializing Datadog profiling (env={dd_env})")
|
|
211
|
+
|
|
212
|
+
# Configure environment variables before importing ddtrace (must be set in environment before importing ddtrace)
|
|
213
|
+
os.environ.setdefault("DD_ENV", dd_env)
|
|
214
|
+
os.environ.setdefault("DD_SERVICE", telemetry_settings.datadog_service_name)
|
|
215
|
+
os.environ.setdefault("DD_VERSION", letta_version)
|
|
216
|
+
os.environ.setdefault("DD_AGENT_HOST", telemetry_settings.datadog_agent_host)
|
|
217
|
+
os.environ.setdefault("DD_TRACE_AGENT_PORT", str(telemetry_settings.datadog_agent_port))
|
|
218
|
+
os.environ.setdefault("DD_PROFILING_ENABLED", "true")
|
|
219
|
+
os.environ.setdefault("DD_PROFILING_MEMORY_ENABLED", str(telemetry_settings.datadog_profiling_memory_enabled).lower())
|
|
220
|
+
os.environ.setdefault("DD_PROFILING_HEAP_ENABLED", str(telemetry_settings.datadog_profiling_heap_enabled).lower())
|
|
221
|
+
|
|
222
|
+
from ddtrace.profiling import Profiler
|
|
223
|
+
|
|
224
|
+
# Initialize and start profiler
|
|
225
|
+
profiler = Profiler(
|
|
226
|
+
env=dd_env,
|
|
227
|
+
service=telemetry_settings.datadog_service_name,
|
|
228
|
+
version=letta_version,
|
|
229
|
+
)
|
|
230
|
+
profiler.start()
|
|
231
|
+
|
|
232
|
+
# Log Git metadata for source code integration
|
|
233
|
+
git_info = ""
|
|
234
|
+
if telemetry_settings.datadog_git_commit_sha:
|
|
235
|
+
git_info = f", commit={telemetry_settings.datadog_git_commit_sha[:8]}"
|
|
236
|
+
if telemetry_settings.datadog_git_repository_url:
|
|
237
|
+
git_info += f", repo={telemetry_settings.datadog_git_repository_url}"
|
|
238
|
+
|
|
239
|
+
logger.info(
|
|
240
|
+
f"Datadog profiling enabled: env={dd_env}, "
|
|
241
|
+
f"service={telemetry_settings.datadog_service_name}, "
|
|
242
|
+
f"agent={telemetry_settings.datadog_agent_host}:{telemetry_settings.datadog_agent_port}{git_info}"
|
|
243
|
+
)
|
|
244
|
+
except Exception as e:
|
|
245
|
+
logger.error(f"Failed to initialize Datadog profiling: {e}", exc_info=True)
|
|
246
|
+
if SENTRY_ENABLED:
|
|
247
|
+
sentry_sdk.capture_exception(e)
|
|
248
|
+
# Don't fail application startup if Datadog initialization fails
|
|
201
249
|
|
|
202
250
|
debug_mode = "--debug" in sys.argv
|
|
203
251
|
app = FastAPI(
|
|
@@ -228,10 +276,67 @@ def create_application() -> "FastAPI":
|
|
|
228
276
|
},
|
|
229
277
|
)
|
|
230
278
|
|
|
279
|
+
# Reasoning for this handler is the default path validation logic returns a pretty gnarly error message
|
|
280
|
+
# because of the uuid4 pattern. This handler rewrites the error message to be more user-friendly and less intimidating.
|
|
281
|
+
@app.exception_handler(RequestValidationError)
|
|
282
|
+
async def custom_request_validation_handler(request: Request, exc: RequestValidationError):
|
|
283
|
+
"""Generalize path `_id` validation messages and include example IDs.
|
|
284
|
+
|
|
285
|
+
- Rewrites string pattern/length mismatches to "primitive-{uuid4}"
|
|
286
|
+
- Preserves stringified `detail` and includes `trace_id`
|
|
287
|
+
- Adds top-level `examples` from `PATH_VALIDATORS` for offending params
|
|
288
|
+
"""
|
|
289
|
+
errors = exc.errors()
|
|
290
|
+
examples_set: set[str] = set()
|
|
291
|
+
content = {"trace_id": get_trace_id() or ""}
|
|
292
|
+
for err in errors:
|
|
293
|
+
fastapi_error_loc = err.get("loc", [])
|
|
294
|
+
# only rewrite path param validation errors (should expand in future)
|
|
295
|
+
if len(fastapi_error_loc) != 2 or fastapi_error_loc[0] != "path":
|
|
296
|
+
continue
|
|
297
|
+
|
|
298
|
+
# re-write the error message
|
|
299
|
+
parameter_name = fastapi_error_loc[1]
|
|
300
|
+
err_type = err.get("type")
|
|
301
|
+
if (
|
|
302
|
+
err_type in {"string_pattern_mismatch", "string_too_short", "string_too_long"}
|
|
303
|
+
and isinstance(parameter_name, str)
|
|
304
|
+
and parameter_name.endswith("_id")
|
|
305
|
+
):
|
|
306
|
+
primitive = parameter_name[:-3]
|
|
307
|
+
validator = PATH_VALIDATORS.get(primitive)
|
|
308
|
+
if validator:
|
|
309
|
+
# simplify default error message
|
|
310
|
+
err["msg"] = f"String should match pattern '{primitive}-{{uuid4}}'"
|
|
311
|
+
|
|
312
|
+
# rewrite as string_pattern_mismatch even if the input length is too short or too long (more intuitive for user)
|
|
313
|
+
if err_type in {"string_too_short", "string_too_long"}:
|
|
314
|
+
# FYI: the pattern is the same as the pattern inthe validator object but for some reason the validator object
|
|
315
|
+
# doesn't let you access it directly (unless you call into pydantic layer)
|
|
316
|
+
err["ctx"] = {"pattern": PRIMITIVE_ID_PATTERNS[primitive].pattern}
|
|
317
|
+
err["type"] = "string_pattern_mismatch"
|
|
318
|
+
|
|
319
|
+
# collect examples for top-level examples field (prevents duplicates and allows for multiple examples for multiple primitives)
|
|
320
|
+
# e.g. if there are 2 malformed agent ids, the examples field will contain 2 examples for the agent primitive
|
|
321
|
+
# e.g. if there is a malformed agent id and malformed folder id, the examples field will contain both examples, for both the agent and folder primitives
|
|
322
|
+
try:
|
|
323
|
+
exs = getattr(validator, "examples", None)
|
|
324
|
+
if exs:
|
|
325
|
+
for ex in exs:
|
|
326
|
+
examples_set.add(ex)
|
|
327
|
+
else:
|
|
328
|
+
examples_set.add(f"{primitive}-123e4567-e89b-42d3-8456-426614174000")
|
|
329
|
+
except Exception:
|
|
330
|
+
examples_set.add(f"{primitive}-123e4567-e89b-42d3-8456-426614174000")
|
|
331
|
+
|
|
332
|
+
# Preserve current API contract: stringified list of errors
|
|
333
|
+
content["detail"] = repr(errors)
|
|
334
|
+
if examples_set:
|
|
335
|
+
content["examples"] = sorted(examples_set)
|
|
336
|
+
return JSONResponse(status_code=422, content=content)
|
|
337
|
+
|
|
231
338
|
async def error_handler_with_code(request: Request, exc: Exception, code: int, detail: str | None = None):
|
|
232
339
|
logger.error(f"{type(exc).__name__}", exc_info=exc)
|
|
233
|
-
if SENTRY_ENABLED:
|
|
234
|
-
sentry_sdk.capture_exception(exc)
|
|
235
340
|
|
|
236
341
|
if not detail:
|
|
237
342
|
detail = str(exc)
|
|
@@ -246,6 +351,8 @@ def create_application() -> "FastAPI":
|
|
|
246
351
|
_error_handler_404_user = partial(_error_handler_404, detail="User not found")
|
|
247
352
|
_error_handler_408 = partial(error_handler_with_code, code=408)
|
|
248
353
|
_error_handler_409 = partial(error_handler_with_code, code=409)
|
|
354
|
+
_error_handler_410 = partial(error_handler_with_code, code=410)
|
|
355
|
+
_error_handler_415 = partial(error_handler_with_code, code=415)
|
|
249
356
|
_error_handler_422 = partial(error_handler_with_code, code=422)
|
|
250
357
|
_error_handler_500 = partial(error_handler_with_code, code=500)
|
|
251
358
|
_error_handler_503 = partial(error_handler_with_code, code=503)
|
|
@@ -263,6 +370,9 @@ def create_application() -> "FastAPI":
|
|
|
263
370
|
app.add_exception_handler(LettaUserNotFoundError, _error_handler_404_user)
|
|
264
371
|
app.add_exception_handler(AgentNotFoundForExportError, _error_handler_404)
|
|
265
372
|
|
|
373
|
+
# 410 Expired errors
|
|
374
|
+
app.add_exception_handler(LettaExpiredError, _error_handler_410)
|
|
375
|
+
|
|
266
376
|
# 408 Timeout errors
|
|
267
377
|
app.add_exception_handler(LettaMCPTimeoutError, _error_handler_408)
|
|
268
378
|
app.add_exception_handler(LettaInvalidMCPSchemaError, _error_handler_400)
|
|
@@ -271,6 +381,10 @@ def create_application() -> "FastAPI":
|
|
|
271
381
|
app.add_exception_handler(ForeignKeyConstraintViolationError, _error_handler_409)
|
|
272
382
|
app.add_exception_handler(UniqueConstraintViolationError, _error_handler_409)
|
|
273
383
|
app.add_exception_handler(IntegrityError, _error_handler_409)
|
|
384
|
+
app.add_exception_handler(PendingApprovalError, _error_handler_409)
|
|
385
|
+
|
|
386
|
+
# 415 Unsupported Media Type errors
|
|
387
|
+
app.add_exception_handler(LettaUnsupportedFileUploadError, _error_handler_415)
|
|
274
388
|
|
|
275
389
|
# 422 Validation errors
|
|
276
390
|
app.add_exception_handler(ValidationError, _error_handler_422)
|
|
@@ -281,6 +395,7 @@ def create_application() -> "FastAPI":
|
|
|
281
395
|
|
|
282
396
|
# 503 Service Unavailable errors
|
|
283
397
|
app.add_exception_handler(OperationalError, _error_handler_503)
|
|
398
|
+
app.add_exception_handler(LettaServiceUnavailableError, _error_handler_503)
|
|
284
399
|
|
|
285
400
|
@app.exception_handler(IncompatibleAgentType)
|
|
286
401
|
async def handle_incompatible_agent_type(request: Request, exc: IncompatibleAgentType):
|
|
@@ -311,8 +426,6 @@ def create_application() -> "FastAPI":
|
|
|
311
426
|
@app.exception_handler(BedrockPermissionError)
|
|
312
427
|
async def bedrock_permission_error_handler(request, exc: BedrockPermissionError):
|
|
313
428
|
logger.error("Bedrock permission denied.")
|
|
314
|
-
if SENTRY_ENABLED:
|
|
315
|
-
sentry_sdk.capture_exception(exc)
|
|
316
429
|
|
|
317
430
|
return JSONResponse(
|
|
318
431
|
status_code=403,
|
|
@@ -440,6 +553,9 @@ def create_application() -> "FastAPI":
|
|
|
440
553
|
logger.warning(f"Failed to setup SQLAlchemy instrumentation: {e}")
|
|
441
554
|
# Don't fail startup if instrumentation fails
|
|
442
555
|
|
|
556
|
+
# Ensure our validation handler overrides tracing's handler when tracing is enabled
|
|
557
|
+
app.add_exception_handler(RequestValidationError, custom_request_validation_handler)
|
|
558
|
+
|
|
443
559
|
for route in v1_routes:
|
|
444
560
|
app.include_router(route, prefix=API_PREFIX)
|
|
445
561
|
# this gives undocumented routes for "latest" and bare api calls.
|
|
@@ -20,6 +20,7 @@ class HeaderParams(BaseModel):
|
|
|
20
20
|
actor_id: Optional[str] = None
|
|
21
21
|
user_agent: Optional[str] = None
|
|
22
22
|
project_id: Optional[str] = None
|
|
23
|
+
sdk_version: Optional[str] = None
|
|
23
24
|
experimental_params: Optional[ExperimentalParams] = None
|
|
24
25
|
|
|
25
26
|
|
|
@@ -27,6 +28,7 @@ def get_headers(
|
|
|
27
28
|
actor_id: Optional[str] = Header(None, alias="user_id"),
|
|
28
29
|
user_agent: Optional[str] = Header(None, alias="User-Agent"),
|
|
29
30
|
project_id: Optional[str] = Header(None, alias="X-Project-Id"),
|
|
31
|
+
sdk_version: Optional[str] = Header(None, alias="X-Stainless-Package-Version"),
|
|
30
32
|
message_async: Optional[str] = Header(None, alias="X-Experimental-Message-Async"),
|
|
31
33
|
letta_v1_agent: Optional[str] = Header(None, alias="X-Experimental-Letta-V1-Agent"),
|
|
32
34
|
) -> HeaderParams:
|
|
@@ -35,6 +37,7 @@ def get_headers(
|
|
|
35
37
|
actor_id=actor_id,
|
|
36
38
|
user_agent=user_agent,
|
|
37
39
|
project_id=project_id,
|
|
40
|
+
sdk_version=sdk_version,
|
|
38
41
|
experimental_params=ExperimentalParams(
|
|
39
42
|
message_async=(message_async == "true") if message_async else None,
|
|
40
43
|
letta_v1_agent=(letta_v1_agent == "true") if letta_v1_agent else None,
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import json
|
|
3
3
|
import queue
|
|
4
|
-
|
|
4
|
+
|
|
5
|
+
from letta.log import get_logger
|
|
6
|
+
|
|
7
|
+
logger = get_logger(__name__)
|
|
5
8
|
from collections import deque
|
|
6
9
|
from datetime import datetime
|
|
7
10
|
from typing import AsyncGenerator, Literal, Optional, Union
|
|
@@ -503,7 +506,7 @@ class StreamingServerInterface(AgentChunkStreamingInterface):
|
|
|
503
506
|
data: {"function_return": "None", "status": "success", "date": "2024-02-29T06:07:50.847262+00:00"}
|
|
504
507
|
"""
|
|
505
508
|
if not chunk.choices or len(chunk.choices) == 0:
|
|
506
|
-
|
|
509
|
+
logger.warning(f"No choices in chunk: {chunk}")
|
|
507
510
|
return None
|
|
508
511
|
|
|
509
512
|
choice = chunk.choices[0]
|
|
@@ -1028,7 +1031,7 @@ class StreamingServerInterface(AgentChunkStreamingInterface):
|
|
|
1028
1031
|
# created=1713216662
|
|
1029
1032
|
# model='gpt-4o-mini-2024-07-18'
|
|
1030
1033
|
# object='chat.completion.chunk'
|
|
1031
|
-
|
|
1034
|
+
logger.warning(f"Couldn't find delta in chunk: {chunk}")
|
|
1032
1035
|
return None
|
|
1033
1036
|
|
|
1034
1037
|
return processed_chunk
|
|
@@ -1255,7 +1258,7 @@ class StreamingServerInterface(AgentChunkStreamingInterface):
|
|
|
1255
1258
|
try:
|
|
1256
1259
|
func_args = parse_json(function_call.function.arguments)
|
|
1257
1260
|
except:
|
|
1258
|
-
|
|
1261
|
+
logger.warning(f"Failed to parse function arguments: {function_call.function.arguments}")
|
|
1259
1262
|
func_args = {}
|
|
1260
1263
|
|
|
1261
1264
|
if (
|
|
@@ -213,6 +213,7 @@ async def create_background_stream_processor(
|
|
|
213
213
|
run_manager: Optional run manager for updating run status
|
|
214
214
|
actor: Optional actor for run status updates
|
|
215
215
|
"""
|
|
216
|
+
stop_reason = None
|
|
216
217
|
if writer is None:
|
|
217
218
|
writer = RedisSSEStreamWriter(redis_client)
|
|
218
219
|
await writer.start()
|
|
@@ -232,6 +233,15 @@ async def create_background_stream_processor(
|
|
|
232
233
|
if is_done:
|
|
233
234
|
break
|
|
234
235
|
|
|
236
|
+
try:
|
|
237
|
+
# sorry for this
|
|
238
|
+
maybe_json_chunk = chunk.split("data: ")[1]
|
|
239
|
+
maybe_stop_reason = json.loads(maybe_json_chunk) if maybe_json_chunk and maybe_json_chunk[0] == "{" else None
|
|
240
|
+
if maybe_stop_reason and maybe_stop_reason.get("message_type") == "stop_reason":
|
|
241
|
+
stop_reason = maybe_stop_reason.get("stop_reason")
|
|
242
|
+
except:
|
|
243
|
+
pass
|
|
244
|
+
|
|
235
245
|
except Exception as e:
|
|
236
246
|
logger.error(f"Error processing stream for run {run_id}: {e}")
|
|
237
247
|
# Write error chunk
|
|
@@ -251,9 +261,14 @@ async def create_background_stream_processor(
|
|
|
251
261
|
if should_stop_writer:
|
|
252
262
|
await writer.stop()
|
|
253
263
|
if run_manager and actor:
|
|
264
|
+
if stop_reason == "cancelled":
|
|
265
|
+
run_status = RunStatus.cancelled
|
|
266
|
+
else:
|
|
267
|
+
run_status = RunStatus.completed
|
|
268
|
+
|
|
254
269
|
await run_manager.update_run_by_id_async(
|
|
255
270
|
run_id=run_id,
|
|
256
|
-
update=RunUpdate(status=
|
|
271
|
+
update=RunUpdate(status=run_status, stop_reason=stop_reason or StopReasonType.end_turn.value),
|
|
257
272
|
actor=actor,
|
|
258
273
|
)
|
|
259
274
|
|
|
@@ -1,14 +1,17 @@
|
|
|
1
1
|
from letta.server.rest_api.routers.v1.agents import router as agents_router
|
|
2
2
|
from letta.server.rest_api.routers.v1.archives import router as archives_router
|
|
3
3
|
from letta.server.rest_api.routers.v1.blocks import router as blocks_router
|
|
4
|
+
from letta.server.rest_api.routers.v1.chat_completions import router as chat_completions_router, router as openai_chat_completions_router
|
|
4
5
|
from letta.server.rest_api.routers.v1.embeddings import router as embeddings_router
|
|
5
6
|
from letta.server.rest_api.routers.v1.folders import router as folders_router
|
|
6
7
|
from letta.server.rest_api.routers.v1.groups import router as groups_router
|
|
7
8
|
from letta.server.rest_api.routers.v1.health import router as health_router
|
|
8
9
|
from letta.server.rest_api.routers.v1.identities import router as identities_router
|
|
10
|
+
from letta.server.rest_api.routers.v1.internal_runs import router as internal_runs_router
|
|
9
11
|
from letta.server.rest_api.routers.v1.internal_templates import router as internal_templates_router
|
|
10
12
|
from letta.server.rest_api.routers.v1.jobs import router as jobs_router
|
|
11
13
|
from letta.server.rest_api.routers.v1.llms import router as llm_router
|
|
14
|
+
from letta.server.rest_api.routers.v1.mcp_servers import router as mcp_servers_router
|
|
12
15
|
from letta.server.rest_api.routers.v1.messages import router as messages_router
|
|
13
16
|
from letta.server.rest_api.routers.v1.providers import router as providers_router
|
|
14
17
|
from letta.server.rest_api.routers.v1.runs import router as runs_router
|
|
@@ -26,10 +29,13 @@ ROUTERS = [
|
|
|
26
29
|
sources_router,
|
|
27
30
|
folders_router,
|
|
28
31
|
agents_router,
|
|
32
|
+
chat_completions_router,
|
|
29
33
|
groups_router,
|
|
30
34
|
identities_router,
|
|
35
|
+
internal_runs_router,
|
|
31
36
|
internal_templates_router,
|
|
32
37
|
llm_router,
|
|
38
|
+
mcp_servers_router,
|
|
33
39
|
blocks_router,
|
|
34
40
|
jobs_router,
|
|
35
41
|
health_router,
|
|
@@ -42,4 +48,5 @@ ROUTERS = [
|
|
|
42
48
|
messages_router,
|
|
43
49
|
voice_router,
|
|
44
50
|
embeddings_router,
|
|
51
|
+
openai_chat_completions_router,
|
|
45
52
|
]
|