Commit Graph

7015 Commits

Author SHA1 Message Date
Shelley Pham
5dc70e48eb Shelley/let 7218 editor should be compatible with typescript [LET-7218] (#9087)
* fix python icon not showing up

* make typescript compatible for updating tools in typescript

* Update flags.ts

* display tools properly in navigation

* add default json schema to newly created tools

* add typescript to code editor

* make editor typescript compatible

* Update ToolsEditor.tsx

* typescript ocmpatible editor

* sandbox stuff

* update breadcrumb icon

* pass in source type to tool simulator

* undo

* Update tool-editor.cy.ts
2026-01-29 12:44:04 -08:00
Charles Packer
e0d9238bb6 fix(core): add check_api_key method to MiniMaxProvider (#9112)
The MiniMaxProvider class was missing a check_api_key() implementation,
causing /v1/providers/check to return a 500 error when validating
MiniMax API keys. The base Provider class raises NotImplementedError.

This adds check_api_key() using the Anthropic client (since MiniMax uses
an Anthropic-compatible API), following the same pattern as AnthropicProvider.

👾 Generated with [Letta Code](https://letta.com)

Co-authored-by: Letta <noreply@letta.com>
2026-01-29 12:44:04 -08:00
Shubham Naik
8f0ac630ab chore: nw [LET-6982] (#9081)
* chore: nw

* chore: more

* feat: redesign details view

* feat: redesign details view

* chore: poll every hour
2026-01-29 12:44:04 -08:00
Sarah Wooders
fb69a96cd6 fix: patch minimax (#9099) 2026-01-29 12:44:04 -08:00
Sarah Wooders
adab8cd9b5 feat: add MiniMax provider support (#9095)
* feat: add MiniMax provider support

Add MiniMax as a new LLM provider using their Anthropic-compatible API.

Key implementation details:
- Uses standard messages API (not beta) - MiniMax supports thinking blocks natively
- Base URL: https://api.minimax.io/anthropic
- Models: MiniMax-M2.1, MiniMax-M2.1-lightning, MiniMax-M2 (all 200K context, 128K output)
- Temperature clamped to valid range (0.0, 1.0]
- All M2.x models treated as reasoning models (support interleaved thinking)

Files added:
- letta/schemas/providers/minimax.py - MiniMax provider schema
- letta/llm_api/minimax_client.py - Client extending AnthropicClient
- tests/test_minimax_client.py - Unit tests (13 tests)
- tests/model_settings/minimax-m2.1.json - Integration test config

🐾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* chore: regenerate API spec with MiniMax provider

🐾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* chore: use MiniMax-M2.1-lightning for CI tests

Switch to the faster/cheaper lightning model variant for integration tests.

🐾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* chore: add MINIMAX_API_KEY to deploy-core command

Co-authored-by: Sarah Wooders <sarahwooders@users.noreply.github.com>

* chore: regenerate web openapi spec with MiniMax provider

Co-authored-by: Sarah Wooders <sarahwooders@users.noreply.github.com>

🐾 Generated with [Letta Code](https://letta.com)

---------

Co-authored-by: Letta <noreply@letta.com>
Co-authored-by: letta-code <248085862+letta-code@users.noreply.github.com>
Co-authored-by: Sarah Wooders <sarahwooders@users.noreply.github.com>
2026-01-29 12:44:04 -08:00
Sarah Wooders
221b4e6279 refactor: add extract_usage_statistics returning LettaUsageStatistics (#9065)
👾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

---------

Co-authored-by: Letta <noreply@letta.com>
2026-01-29 12:44:04 -08:00
cthomas
2bccd36382 Revert "fix: ensure stop_reason is always set and reduce noisy logs (… (#9086)
Revert "fix: ensure stop_reason is always set and reduce noisy logs (#9046)"

This reverts commit 4241a360579440d2697124ba69061d0e46ecc5e9.

**Problem:**
After the original change, caren-code-agent reported streams hanging
indefinitely. The trace shows ttft (time to first token) succeeds, but
the stream never closes.

**Root Cause (suspected):**
The change modified `is_complete=is_done` to `is_complete=saw_done`,
meaning error events no longer mark the stream as complete immediately.
This may cause timing issues where clients wait for more data before
the finalizer runs.

**Fix:**
Revert to the defensive "belt-and-suspenders" approach that always
appends [DONE]. The noisy logs are preferable to hanging streams.

The original comment noted: "Even if a previous chunk set `complete`,
an extra [DONE] is harmless and ensures SDKs that rely on explicit
[DONE] will exit."

👾 Generated with [Letta Code](https://letta.com)

Co-authored-by: Letta <noreply@letta.com>
2026-01-29 12:44:04 -08:00
cthomas
18274b5a42 fix: handle null step_id on approval request messages (#9074)
**Problem:**
Runs failed with error:
```
Argument step_id does not match type <class 'str'>; is None of type <class 'NoneType'>
```

This happened when processing approval responses where the original
approval request message had `step_id=None`.

**Root Cause:**
Line 672 in `_step()` directly used `approval_request.step_id`:
```python
step_id = approval_request.step_id  # Can be None!
step_metrics = await self.step_manager.get_step_metrics_async(step_id=step_id, ...)
```

`Message.step_id` is `Optional[str]` (default None), but `get_step_metrics_async`
has `step_id: str` with `@enforce_types` validation.

Old approval messages or edge cases could have `step_id=None`, causing
the enforce_types decorator to reject the call.

**Fix:**
Check if `step_id is None` and generate a new step_id + initialize step
checkpoint if needed, instead of assuming step_id always exists.

**Note:**
Similar issue exists in letta_agent_v2.py and temporal agents, but v2
is deprecated.

👾 Generated with [Letta Code](https://letta.com)

Co-authored-by: Letta <noreply@letta.com>
2026-01-29 12:44:04 -08:00
cthomas
3e49cf5d44 fix: load default provider config when summarizer uses different prov… (#9051)
fix: load default provider config when summarizer uses different provider

**Problem:**
Summarization failed when agent used one provider (e.g., Google AI) but
summarizer config specified a different provider (e.g., Anthropic):

```python
# Agent LLM config
model_endpoint_type='google_ai', handle='gemini-something/gemini-2.5-pro',
context_window=100000

# Summarizer config
model='anthropic/claude-haiku-4-5-20251001'

# Bug: Resulting summarizer_llm_config mixed Google + Anthropic settings
model='claude-haiku-4-5-20251001', model_endpoint_type='google_ai',  #  Wrong endpoint!
context_window=100000  #  Google's context window, not Anthropic's default!
```

This sent Claude requests to Google AI endpoints with incorrect parameters.

**Root Cause:**
`_build_summarizer_llm_config()` always copied the agent's LLM config as base,
then patched model/provider fields. But this kept all provider-specific settings
(endpoint, context_window, etc.) from the wrong provider.

**Fix:**
1. Parse provider_name from summarizer handle
2. Check if it matches agent's model_endpoint_type (or provider_name for custom)
3. **If YES** → Use agent config as base, override model/handle (same provider)
4. **If NO** → Load default config via `provider_manager.get_llm_config_from_handle()` (new provider)

**Example Flow:**
```python
# Agent: google_ai/gemini-2.5-pro
# Summarizer: anthropic/claude-haiku

provider_name = "anthropic"  # Parsed from handle
provider_matches = ("anthropic" == "google_ai")  # False 

# Different provider → load default Anthropic config
base = await provider_manager.get_llm_config_from_handle(
    handle="anthropic/claude-haiku",
    actor=self.actor
)
# Returns: model_endpoint_type='anthropic', endpoint='https://api.anthropic.com', etc. 
```

**Result:**
- Summarizer with different provider gets correct default config
- No more mixing Google endpoints with Anthropic models
- Same-provider summarizers still inherit agent settings efficiently

👾 Generated with [Letta Code](https://letta.com)

Co-authored-by: Letta <noreply@letta.com>
2026-01-29 12:44:04 -08:00
Shubham Naik
55a89398e1 chore: rebuild api requests (#9069) 2026-01-29 12:44:04 -08:00
github-actions[bot]
194c743223 refactor: rename stream to streaming in ConversationMessageRequest (#9063) 2026-01-29 12:44:04 -08:00
github-actions[bot]
1d1bb29a43 feat: add override_model support for agent file import (#9058) 2026-01-29 12:44:04 -08:00
Charles Packer
82c01368fc feat: add conversation_id to message search results (#9056)
* feat: add conversation_id to message search results

Add conversation_id field to all *MessageListResult classes
(SystemMessageListResult, UserMessageListResult, ReasoningMessageListResult,
AssistantMessageListResult) so that conversation IDs are returned from
the /messages/search endpoint alongside agent IDs.

Fixes #9055

Co-authored-by: Charles Packer <cpacker@users.noreply.github.com>

* chore: regenerate SDK and OpenAPI spec

Regenerate autogenerated files after adding conversation_id to
message search result schemas.

Co-authored-by: Sarah Wooders <sarahwooders@users.noreply.github.com>

---------

Co-authored-by: letta-code <248085862+letta-code@users.noreply.github.com>
Co-authored-by: Charles Packer <cpacker@users.noreply.github.com>
Co-authored-by: Sarah Wooders <sarahwooders@users.noreply.github.com>
2026-01-29 12:44:04 -08:00
Sarah Wooders
6c415b27f8 feat: add non-streaming option for conversation messages (#9044)
* feat: add non-streaming option for conversation messages

- Add ConversationMessageRequest with stream=True default (backwards compatible)
- stream=true (default): SSE streaming via StreamingService
- stream=false: JSON response via AgentLoop.load().step()

🤖 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* chore: regenerate API schema for ConversationMessageRequest

---------

Co-authored-by: Letta <noreply@letta.com>
2026-01-29 12:44:04 -08:00
cthomas
208992170c fix: gracefully skip assistant messages with empty content in LLM for… (#9050)
fix: gracefully skip assistant messages with empty content in LLM format conversion

**Problem:**
Context window calculation crashed with AssertionError when converting messages
to Google/Anthropic/OpenAI format:
```
AssertionError at line 2047: assert self.tool_calls is not None or
text_content is not None or len(self.content) > 1
```

This happened when loading agents with old/malformed messages that had
`content=None` or `content=[]` in the database.

**Root Cause:**
The Message ORM model allows `content: Optional[List[...]] = None` (line 252),
but format conversion methods assumed content would always have extractable text
or tool calls.

Scenarios that triggered crashes:
1. Assistant message with `content=None` (old migrations/edge cases)
2. Assistant message with `content=[]` (message creation bugs)
3. Assistant message with single non-text content that doesn't match extraction logic

**Fix:**
Replaced assertions with defensive checks in 3 conversion methods:

1. `to_google_dict()` (line 2054) - Return None to skip unconvertible messages
2. `to_openai_responses_api_dicts()` (line 1476) - Return early to skip
3. `to_anthropic_dict()` (line 1794) - Return None to skip

Pattern: Check for empty content, return None/early to skip gracefully.

**Result:**
- Context window calculation no longer crashes on malformed/old messages
- Messages with no convertible content are silently skipped
- Consistent with existing Anthropic reasoning-only message handling (line 1308)

👾 Generated with [Letta Code](https://letta.com)

Co-authored-by: Letta <noreply@letta.com>
2026-01-29 12:44:04 -08:00
cthomas
4c2253dc76 fix: use repr() fallback for empty exception messages in error logging (#9047)
**Problem:**
Error logs showed empty detail fields when exceptions had no message:
```
Error during step processing:
Run run-xxx stopped with unknown error: , error_data: {...'detail': ''}
```

This made debugging production issues difficult as the actual error type
was hidden.

**Root Cause:**
Python exceptions created with no arguments (e.g., `Exception()` or caught
and re-raised in certain ways) have `str(e) == ""`:

```python
e = Exception()
str(e)   # Returns ""
repr(e)  # Returns "Exception()"
```

When exceptions with empty string representations were caught, all logging
and error messages showed blank details.

**Fix:**
Use `str(e) or repr(e)` fallback pattern in 3 places:
1. `letta_agent_v3.py` stream() exception handler (line 406)
2. `letta_agent_v3.py` step() exception handler (line 928)
3. `streaming_service.py` generic exception handler (line 469)

**Result:**
- Error logs now show `Exception()` or similar instead of empty string
- Helps identify exception types even when message is missing
- Better production debugging without changing exception handling logic

👾 Generated with [Letta Code](https://letta.com)

Co-authored-by: Letta <noreply@letta.com>
2026-01-29 12:44:04 -08:00
cthomas
2a2e777807 fix: ensure stop_reason is always set and reduce noisy logs (#9046)
fix: consume [DONE] token after error events to prevent forced finalizer append

**Problem:**
Stream finalizer was frequently logging warning and appending forced [DONE]:
```
[Stream Finalizer] Appending forced [DONE] for run=run-xxx (saw_error=True,
saw_done=False, final_stop_reason=llm_api_error)
```

This happened on every error, even though streaming_service.py already yields
[DONE] after all error events.

**Root Cause:**
Line 266: `is_done = saw_done or saw_error` caused loop to break immediately
after seeing error event, BEFORE consuming the [DONE] chunk that follows:

```python
is_done = saw_done or saw_error
await writer.write_chunk(...)
if is_done:  # Breaks on error!
    break
```

Sequence:
1. streaming_service.py yields: `event: error\ndata: {...}\n\n`
2. Redis reader sees error → sets `saw_error=True`
3. Sets `is_done=True` and breaks
4. Never reads next chunk: `data: [DONE]\n\n`
5. Finalizer runs → `saw_done=False` → appends forced [DONE]

**Fix:**
1. Only break when `saw_done=True` (not `saw_error`) → allows consuming [DONE]
2. Only run finalizer when `saw_done=False` → reduces log noise

**Result:**
- [DONE] now consumed naturally from streaming_service.py error handlers
- Finalizer warning only appears when truly needed (fallback cases)
- Cleaner production logs

👾 Generated with [Letta Code](https://letta.com)

Co-authored-by: Letta <noreply@letta.com>
2026-01-29 12:44:04 -08:00
cthomas
ca40eff7bc fix: ensure stop_reason is always set when marking runs as failed (#9045)
**Problem:**
Production error showed runs being marked as failed with stop_reason=None,
which violates LettaStopReason's Pydantic schema (requires valid enum value).
This caused cascading validation errors that got stored in metadata.

Example error:
```
Run is already in a terminal state failed with stop reason None, but is being
updated with data {'status': 'failed', 'stop_reason': None, 'metadata':
{'error': "1 validation error for LettaStopReason\nstop_reason Input should
be 'end_turn', 'error', ... [type=enum, input_value=None]"}}
```

**Root Causes:**
1. routers/v1/agents.py had 3 exception handlers creating RunUpdate(status=failed)
   without stop_reason
2. Success path assumed result.stop_reason always exists (AttributeError if None)
3. run_manager.py tried to create LettaStopReason(stop_reason=None) when
   refreshing result messages

**Fixes:**
1. Added stop_reason=StopReasonType.error to 3 exception handlers
2. Added defensive None checks before accessing result.stop_reason.stop_reason
3. Added fallback to StopReasonType.error when pydantic_run.stop_reason is None

**Trigger:**
OpenAI BadRequestError for invalid tool schema → exception handlers marked
run as failed without stop_reason → validation error when constructing response

👾 Generated with [Letta Code](https://letta.com)

Co-authored-by: Letta <noreply@letta.com>
2026-01-29 12:44:04 -08:00
Ari Webb
5533c723df fix: bedrock third time (#9043) 2026-01-29 12:44:04 -08:00
Ari Webb
e5afbd0972 fix: base url wrong (#9040) 2026-01-29 12:44:04 -08:00
cthomas
57ab117437 feat: dedupe approval response retries on server (#9038) 2026-01-29 12:44:04 -08:00
Sarah Wooders
25e9539a6e feat: add batch passage create and optional search query (#8866) 2026-01-29 12:44:04 -08:00
github-actions[bot]
179a1df524 feat: add conversation compact endpoint to SDK and add integration tests (#9025) 2026-01-29 12:44:04 -08:00
Shubham Naik
8ced2e0c82 Shub/let 7138 support custom feeds that recieve data via an endpoint [LET-7138] (#9027)
* feat: support custom endpoint

* feat: support custom endpoint

* chore: add webhook

* chore: add webhook

* chore: fix types

* chore: fix types

* chore: docs
2026-01-29 12:44:04 -08:00
cthomas
c162de5127 fix: use shared event + .athrow() to properly set stream_was_cancelle… (#9019)
fix: use shared event + .athrow() to properly set stream_was_cancelled flag

**Problem:**
When a run is cancelled via /cancel endpoint, `stream_was_cancelled` remained
False because `RunCancelledException` was raised in the consumer code (wrapper),
which closes the generator from outside. This causes Python to skip the
generator's except blocks and jump directly to finally with the wrong flag value.

**Solution:**
1. Shared `asyncio.Event` registry for cross-layer cancellation signaling
2. `cancellation_aware_stream_wrapper` sets the event when cancellation detected
3. Wrapper uses `.athrow()` to inject exception INTO generator (not consumer-side raise)
4. All streaming interfaces check event in `finally` block to set flag correctly
5. `streaming_service.py` handles `RunCancelledException` gracefully, yields [DONE]

**Changes:**
- streaming_response.py: Event registry + .athrow() injection + graceful handling
- openai_streaming_interface.py: 3 classes check event in finally
- gemini_streaming_interface.py: Check event in finally
- anthropic_*.py: Catch RunCancelledException
- simple_llm_stream_adapter.py: Create & pass event to interfaces
- streaming_service.py: Handle RunCancelledException, yield [DONE], skip double-update
- routers/v1/{conversations,runs}.py: Pass event to wrapper
- integration_test_human_in_the_loop.py: New test for approval + cancellation

**Tests:**
- test_tool_call with cancellation (OpenAI models) 
- test_approve_with_cancellation (approval flow + concurrent cancel) 

**Known cosmetic warnings (pre-existing):**
- "Run already in terminal state" - agent loop tries to update after /cancel
- "Stream ended without terminal event" - background streaming timing race

👾 Generated with [Letta Code](https://letta.com)

Co-authored-by: Letta <noreply@letta.com>
2026-01-29 12:44:04 -08:00
Ari Webb
5ca0f55079 fix: fix bedrock again (#9021) 2026-01-29 12:44:04 -08:00
Kian Jones
e3fb00f970 feat(crouton): add orgId, userId, Compaction_Settings and LLM_Config (#9022)
* LC one shot?

* api changes

* fix summarizer nameerror
2026-01-29 12:44:04 -08:00
Kian Jones
194fa7d1c6 fix: anthropic message packing bugs (#9017)
* fix: anthroppic message packing bugs - traling whitespace and final assistant message missing thinking

* revert bug Caren will fix upstream?
2026-01-29 12:44:04 -08:00
Ari Webb
5c06918042 fix: don't need embedding model for self hosted [LET-7009] (#8935)
* fix: don't need embedding model for self hosted

* stage publish api

* passes tests

* add test

* remove unnecessary upgrades

* update revision order db migrations

* add timeout for ci
2026-01-29 12:44:04 -08:00
Shubham Naik
16e3f10a56 Shub/let 7147 improved channel selector [LET-7147] (#9002)
* feat; improve selector

* chore: next

* chore: next

* wah

* wah

* wah

* chore: next

* chore: fix

* chore: noverify

* chroe; imporve selctor

* chore: update api
2026-01-29 12:44:04 -08:00
Shubham Naik
6d453ea586 feat: fix template creation bogs [LET-7165] (#9015)
feat: fix template creation bogs
2026-01-29 12:44:02 -08:00
Kian Jones
2bb4caffc3 fix: remove unused embedding generation (#9013)
* remove unused embedding generation

* prevent double embed

* fix embedding dimension comparison and valueerror
2026-01-29 12:43:53 -08:00
Shubham Naik
dbc4f88701 chore: add better error logging (#8981) 2026-01-29 12:43:53 -08:00
Shelley Pham
4353df683e Shelley/let 7155 favorites tagged should be user scoped [LET-7155] (#9003)
* make favorite tag a const

* add favorite:user:{userId} for favorites

* favorite agent upon initial creation

* rename const

* add eslint ignore

* expect favorite tag
2026-01-29 12:43:53 -08:00
Ari Webb
b5e93ab6d1 fix: bedrock config state (#9005) 2026-01-29 12:43:53 -08:00
Kian Jones
273ca9ec44 feat(tests): add crouton telemetry tests (#9000)
* test: add comprehensive provider trace telemetry tests

Add two test files for provider trace telemetry:

1. test_provider_trace.py - Integration tests for:
   - Basic agent steps (streaming and non-streaming)
   - Tool calls
   - Telemetry context fields (agent_id, agent_tags, step_id, run_id)
   - Multi-step conversations
   - Request/response JSON content

2. test_provider_trace_summarization.py - Unit tests for:
   - simple_summary() telemetry context passing
   - summarize_all() telemetry pass-through
   - summarize_via_sliding_window() telemetry pass-through
   - Summarizer class runtime vs constructor telemetry
   - LLMClient.set_telemetry_context() method

🤖 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* test: add telemetry tests for tool generation, adapters, and agent versions

Add comprehensive unit tests for provider trace telemetry:

- TestToolGenerationTelemetry: Verify /generate-tool endpoint sets
  call_type="tool_generation" and has no agent context
- TestLLMClientTelemetryContext: Verify LLMClient.set_telemetry_context
  accepts all telemetry fields
- TestAdapterTelemetryAttributes: Verify base adapter and subclasses
  (LettaLLMRequestAdapter, LettaLLMStreamAdapter) support telemetry attrs
- TestSummarizerTelemetry: Verify Summarizer stores and passes telemetry
- TestAgentAdapterInstantiation: Verify LettaAgentV2 creates Summarizer
  with correct agent_id

🤖 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* ci: add provider trace telemetry tests to unit test workflow

Add the new provider trace test files to the CI matrix:
- test_provider_trace_backends.py
- test_provider_trace_summarization.py
- test_provider_trace_agents.py

🤖 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* fix: update socket backend test to match new record structure

The socket backend record structure changed - step_id/run_id are now
at top level, and model/usage are nested in request/response objects.

🤖 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* fix: add step_id to V1 agent telemetry context

Pass step_id to set_telemetry_context in both streaming and non-streaming
paths in LettaAgent (v1). The step_id is available via step_metrics.id
in the non-streaming path and passed explicitly in the streaming path.

🤖 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

---------

Co-authored-by: Letta <noreply@letta.com>
2026-01-29 12:43:53 -08:00
Ari Webb
5c1512237f fix: restore deleted provider session conflicts (#9001) 2026-01-29 12:43:53 -08:00
Kian Jones
4d256b3399 feat: add agent_id, run_id, step_id to summarization provider traces (#8996)
* feat: add agent_id, run_id, step_id to summarization provider traces

Summarization LLM calls were missing telemetry context (agent_id,
agent_tags, run_id, step_id), making it impossible to attribute
summarization costs to specific agents or trace them back to the
step that triggered compaction.

Changes:
- Add step_id param to simple_summary() and set_telemetry_context()
- Add agent_id, agent_tags, run_id, step_id to summarize_all() and
  summarize_via_sliding_window()
- Update Summarizer class to accept and pass telemetry context
- Update LettaAgentV3.compact() to pass full telemetry context
- Update LettaAgentV2.summarize_conversation_history() with run_id/step_id
- Update LettaAgent (v1) streaming methods with run_id param
- Add run_id/step_id to SummarizeParams for Temporal activities

🤖 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* fix: update test mock to accept new summarization params

🤖 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

---------

Co-authored-by: Letta <noreply@letta.com>
2026-01-29 12:43:53 -08:00
Kian Jones
1ab21af725 fix: safer type coersion for tools (#8990)
* mvp

* perfrom type coercion in sandbox

* fix: safely resolve typing annotations on host

Use an AST whitelist for generic annotations to avoid eval while keeping list/dict coercion working.

👾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

---------

Co-authored-by: Letta <noreply@letta.com>
2026-01-29 12:43:53 -08:00
Ari Webb
2e826577d9 fix: fix zai and others byok (#8991)
* fix: fix zai and other byok providers

* fix test

* get endpoint from typed provider and add test

* also add base_url on provider create
2026-01-29 12:43:53 -08:00
Kian Jones
7133083b81 fix: agent_tags for provider traces (#8989)
* add include tags

* include agent_tags and pass them into the adapter
2026-01-29 12:43:53 -08:00
Charles Packer
2fc592e0b6 feat(core): add image support in tool returns [LET-7140] (#8985)
* feat(core): add image support in tool returns [LET-7140]

Enable tool_return to support both string and ImageContent content parts,
matching the pattern used for user message inputs. This allows tools
executed client-side to return images back to the agent.

Changes:
- Add LettaToolReturnContentUnion type for text/image content parts
- Update ToolReturn schema to accept Union[str, List[content parts]]
- Update converters for each provider:
  - OpenAI Chat Completions: placeholder text for images
  - OpenAI Responses API: full image support
  - Anthropic: full image support with base64
  - Google: placeholder text for images
- Add resolve_tool_return_images() for URL-to-base64 conversion
- Make create_approval_response_message_from_input() async

🐾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* fix(core): support images in Google tool returns as sibling parts

Following the gemini-cli pattern: images in tool returns are sent as
sibling inlineData parts alongside the functionResponse, rather than
inside it.

🐾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* test(core): add integration tests for multi-modal tool returns [LET-7140]

Tests verify that:
- Models with image support (Anthropic, OpenAI Responses API) can see
  images in tool returns and identify the secret text
- Models without image support (Chat Completions) get placeholder text
  and cannot see the actual image content
- Tool returns with images persist correctly in the database

Uses secret.png test image containing hidden text "FIREBRAWL" that
models must identify to pass the test.

Also fixes misleading comment about Anthropic only supporting base64
images - they support URLs too, we just pre-resolve for consistency.

🐾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* refactor: simplify tool return image support implementation

Reduce code verbosity while maintaining all functionality:
- Extract _resolve_url_to_base64() helper in message_helper.py (eliminates duplication)
- Add _get_text_from_part() helper for text extraction
- Add _get_base64_image_data() helper for image data extraction
- Add _tool_return_to_google_parts() to simplify Google implementation
- Add _image_dict_to_data_url() for OpenAI Responses format
- Use walrus operator and list comprehensions where appropriate
- Add integration_test_multi_modal_tool_returns.py to CI workflow

Net change: -120 lines while preserving all features and test coverage.

👾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* fix(tests): improve prompt for multi-modal tool return tests

Make prompts more direct to reduce LLM flakiness:
- Simplify tool description: "Retrieves a secret image with hidden text. Call this function to get the image."
- Change user prompt from verbose request to direct command: "Call the get_secret_image function now."
- Apply to both test methods

This reduces ambiguity and makes tool calling more reliable across different LLM models.

👾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* fix bugs

* test(core): add google_ai/gemini-2.0-flash-exp to multi-modal tests

Add Gemini model to test coverage for multi-modal tool returns. Google AI already supports images in tool returns via sibling inlineData parts.

👾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* fix(ui): handle multi-modal tool_return type in frontend components

Convert Union<string, LettaToolReturnContentUnion[]> to string for display:
- ViewRunDetails: Convert array to '[Image here]' placeholder
- ToolCallMessageComponent: Convert array to '[Image here]' placeholder

Fixes TypeScript errors in web, desktop-ui, and docker-ui type-checks.

👾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

---------

Co-authored-by: Letta <noreply@letta.com>
Co-authored-by: Caren Thomas <carenthomas@gmail.com>
2026-01-29 12:43:53 -08:00
Ari Webb
4ec6649caf feat: byok provider models in db also (#8317)
* feat: byok provider models in db also

* make tests and sync api

* fix inconsistent state with recreating provider of same name

* fix sync on byok creation

* update revision

* move stripe code for testing purposes

* revert

* add refresh byok models endpoint

* just stage publish api

* add tests

* reorder revision

* add test for name clashes
2026-01-29 12:43:53 -08:00
Christina Tong
fa92f711fe add conversation_id to message obj before persisting (#8984) 2026-01-29 12:43:53 -08:00
Kevin Lin
b5519f02fb feat: make tool return messages more explicit [LET-7145] (#8986)
prompt
2026-01-29 12:43:53 -08:00
cthomas
3f8f2e622a fix: filter our reasoning for groq client [LET-7135] (#8982)
fix: filter our reasoning for groq client
2026-01-29 12:43:53 -08:00
Christina Tong
0333ff0614 fix: max tokens and context window size [LET-6481] (#8298)
* fix: max tokens [LET-6481]

* remove print statements

* update

* simplofy fallback

* address comments async

* update other helpers

* update pyproject,.toml

* update pyproject w async lru

* oopen ai internal async methods

* update

* update uv lock
2026-01-29 12:43:53 -08:00
Charles Packer
238894eebd fix(core): disable MCP stdio servers by default (#8969)
* fix(core): disable MCP stdio servers by default

Stdio MCP servers spawn local processes on the host, which is not
suitable for multi-tenant or shared server deployments. This change:

- Changes `mcp_disable_stdio` default from False to True
- Enforces the setting in `get_mcp_client()` and `create_mcp_server_from_config()`
- Users running local/single-user deployments can set MCP_DISABLE_STDIO=false
  to enable stdio-based MCP servers (e.g., for npx/uvx tools)

👾 Generated with [Letta Code](https://letta.com)

Co-Authored-By: Letta <noreply@letta.com>

* update ci

* push

---------

Co-authored-by: Letta <noreply@letta.com>
Co-authored-by: jnjpng <jin@letta.com>
Co-authored-by: Letta Bot <jinjpeng@gmail.com>
2026-01-29 12:43:53 -08:00
Ari Webb
5645ca8107 fix: use labels for error messages for builtin memory tools [LET-7095] (#8941)
* fix: use labels for error messages for builtin memory tools

* catch specific error
2026-01-29 12:43:53 -08:00
Devansh Jain
dfa6ee0c23 feat: add SGLang support (#8838)
* add sglang support

* add tests

* normalize base url

* cleanup

* chore: regenerate autogenerated API files for sglang support
2026-01-29 12:43:51 -08:00