feat: add zai proxy LET-6543 (#6836)

feat: add zai proxy
This commit is contained in:
cthomas
2025-12-12 16:24:48 -08:00
committed by Caren Thomas
parent bce1749408
commit efac48e9ea
5 changed files with 889 additions and 473 deletions

View File

@@ -858,138 +858,3 @@ async def capture_and_persist_messages(
"messages_created": len(response_messages),
"run_ids": run_ids,
}
async def _backfill_agent_project_id(server, agent, actor, project_id: str):
"""
Temporary helper to backfill project_id for legacy agents.
TODO(@caren): Remove this function after all existing Claude Code agents have been backfilled.
Args:
server: SyncServer instance
agent: Agent to update
actor: Actor performing the operation
project_id: Project ID to set
Returns:
Updated agent or original agent if update fails
"""
from letta.schemas.agent import UpdateAgent
try:
updated_agent = await server.update_agent_async(
agent_id=agent.id,
request=UpdateAgent(project_id=project_id),
actor=actor,
)
logger.info(f"[Backfill] Successfully updated agent {agent.id} with project_id {project_id}")
return updated_agent
except Exception as e:
logger.warning(f"[Backfill] Failed to update agent project_id: {e}. Continuing with in-memory update.")
# Fallback: continue with in-memory update
agent.project_id = project_id
return agent
async def get_or_create_claude_code_agent(
server,
actor,
project_id: Optional[str] = None,
):
"""
Get or create a special agent for Claude Code sessions.
Args:
server: SyncServer instance
actor: Actor performing the operation (user ID)
project_id: Optional project ID to associate the agent with
Returns:
Agent ID
"""
from letta.schemas.agent import CreateAgent
# Create short user identifier from UUID (first 8 chars)
if actor:
user_short_id = str(actor.id)[:8] if hasattr(actor, "id") else str(actor)[:8]
else:
user_short_id = "default"
agent_name = f"claude-code-{user_short_id}"
try:
# Try to find existing agent by name (most reliable)
# Note: Search by name only, not tags, since name is unique and more reliable
logger.debug(f"Searching for agent with name: {agent_name}")
agents = await server.agent_manager.list_agents_async(
actor=actor,
limit=10, # Get a few in case of duplicates
name=agent_name,
include=["agent.blocks", "agent.managed_group", "agent.tags"],
)
# list_agents_async returns a list directly, not an object with .agents
logger.debug(f"Agent search returned {len(agents) if agents else 0} results")
if agents and len(agents) > 0:
# Return the first matching agent
logger.info(f"Found existing Claude Code agent: {agents[0].id} (name: {agent_name})")
agent = agents[0]
# Temporary patch: Fix project_id if it's missing (legacy bug)
# TODO(@caren): Remove this after all existing Claude Code agents have been backfilled
if not agent.project_id and project_id:
logger.info(f"[Backfill] Agent {agent.id} missing project_id, backfilling with {project_id}")
agent = await _backfill_agent_project_id(server, agent, actor, project_id)
return agent
else:
logger.debug(f"No existing agent found with name: {agent_name}")
except Exception as e:
logger.warning(f"Could not find existing agent: {e}", exc_info=True)
# Create new agent
try:
logger.info(f"Creating new Claude Code agent: {agent_name} with project_id: {project_id}")
# Create minimal agent config
agent_config = CreateAgent(
name=agent_name,
description="Agent for capturing Claude Code conversations",
memory_blocks=[
{
"label": "human",
"value": "This is my section of core memory devoted to information about the human.\nI don't yet know anything about them.\nWhat's their name? Where are they from? What do they do? Who are they?\nI should update this memory over time as I interact with the human and learn more about them.",
"description": "A memory block for keeping track of the human (user) the agent is interacting with.",
},
{
"label": "persona",
"value": "This is my section of core memory devoted to information myself.\nThere's nothing here yet.\nI should update this memory over time as I develop my personality.",
"description": "A memory block for storing the agent's core personality details and behavior profile.",
},
{
"label": "project",
"value": "This is my section of core memory devoted to information about what the agent is working on.\nI don't yet know anything about it.\nI should update this memory over time with high level understanding and learnings.",
"description": "A memory block for storing the information about the project the agent is working on.",
},
],
tags=["claude-code"],
enable_sleeptime=True,
agent_type="letta_v1_agent",
model="anthropic/claude-sonnet-4-5-20250929",
embedding="openai/text-embedding-ada-002",
project_id=project_id,
)
new_agent = await server.create_agent_async(
request=agent_config,
actor=actor,
)
logger.info(f"Created Claude Code agent {new_agent.name}: {new_agent.id}")
return new_agent
except Exception as e:
logger.exception(f"Failed to create Claude Code agent: {e}")
raise