Files
letta-server/letta/schemas/letta_stop_reason.py
Ari Webb 0a8a8fda54 feat: add credit verification before agent message endpoints [LET-XXXX] (#9433)
* feat: add credit verification before agent message endpoints

Add credit verification checks to message endpoints to prevent
execution when organizations have insufficient credits.

- Add InsufficientCreditsError exception type
- Add CreditVerificationService that calls step-orchestrator API
- Add credit checks to /agents/{id}/messages endpoints
- Add credit checks to /conversations/{id}/messages endpoint

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

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

* surface error in ade

* do per step instead

* parallel check

* parallel to step

* small fixes

* stage publish api

* fixes

* revert unnecessary frontend changes

* insufficient credits stop reason

---------

Co-authored-by: Letta <noreply@letta.com>
2026-02-24 10:52:07 -08:00

59 lines
2.0 KiB
Python

from enum import Enum
from typing import Literal
from pydantic import BaseModel, Field
from letta.schemas.enums import RunStatus
class StopReasonType(str, Enum):
end_turn = "end_turn"
error = "error"
llm_api_error = "llm_api_error"
invalid_llm_response = "invalid_llm_response"
invalid_tool_call = "invalid_tool_call"
max_steps = "max_steps"
max_tokens_exceeded = "max_tokens_exceeded"
no_tool_call = "no_tool_call"
tool_rule = "tool_rule"
cancelled = "cancelled"
insufficient_credits = "insufficient_credits"
requires_approval = "requires_approval"
context_window_overflow_in_system_prompt = "context_window_overflow_in_system_prompt"
@property
def run_status(self) -> RunStatus:
if self in (
StopReasonType.end_turn,
StopReasonType.max_steps,
StopReasonType.tool_rule,
StopReasonType.requires_approval,
):
return RunStatus.completed
elif self in (
StopReasonType.error,
StopReasonType.invalid_tool_call,
StopReasonType.no_tool_call,
StopReasonType.invalid_llm_response,
StopReasonType.llm_api_error,
# Treat context/token limit exhaustion as an error state (same as llm_api_error)
StopReasonType.max_tokens_exceeded,
StopReasonType.context_window_overflow_in_system_prompt,
):
return RunStatus.failed
elif self == StopReasonType.cancelled:
return RunStatus.cancelled
elif self == StopReasonType.insufficient_credits:
return RunStatus.failed
else:
raise ValueError("Unknown StopReasonType")
class LettaStopReason(BaseModel):
"""
The stop reason from Letta indicating why agent loop stopped execution.
"""
message_type: Literal["stop_reason"] = Field("stop_reason", description="The type of the message.")
stop_reason: StopReasonType = Field(..., description="The reason why execution stopped.")