feat: 增加基础模型设置

This commit is contained in:
ViperEkura 2026-04-16 20:19:41 +08:00
parent 9edf4dac9c
commit 2f9deb40ae
4 changed files with 626 additions and 14 deletions

122
luxx/agent/agent.py Normal file
View File

@ -0,0 +1,122 @@
"""Agent module for autonomous task execution"""
from enum import Enum
import logging
from dataclasses import dataclass, field
from datetime import datetime
from typing import Dict, List, Optional, Any
from luxx.agent.task import Task
from luxx.utils.helpers import generate_id
logger = logging.getLogger(__name__)
class AgentStatus(Enum):
"""Agent status"""
READY = "ready"
RUNNING = "running"
BLOCK = "block"
TERMINATED = "terminated"
@dataclass
class Agent:
"""Agent"""
id: str
name: str
description: str = ""
instructions: str = ""
tools: List[str] = field(default_factory=list)
current_task: Optional[Task] = None
status: AgentStatus = AgentStatus.READY
result: Optional[Dict[str, Any]] = None
error: Optional[str] = None
created_at: datetime = field(default_factory=datetime.now)
updated_at: datetime = field(default_factory=datetime.now)
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary"""
return {
"id": self.id,
"name": self.name,
"description": self.description,
"instructions": self.instructions,
"tools": self.tools,
"current_task": self.current_task.to_dict() if self.current_task else None,
"status": self.status,
"result": self.result,
"error": self.error,
"created_at": self.created_at.isoformat() if self.created_at else None,
"updated_at": self.updated_at.isoformat() if self.updated_at else None
}
class AgentService:
"""Agent service for managing agent instances"""
def __init__(self):
self._agents: Dict[str, Agent] = {}
def create_agent(
self,
name: str,
description: str = "",
instructions: str = "",
tools: List[str] = None
) -> Agent:
"""Create a new agent instance"""
agent_id = generate_id("agent")
agent = Agent(
id=agent_id,
name=name,
description=description,
instructions=instructions,
tools=tools or []
)
self._agents[agent_id] = agent
logger.info(f"Created agent: {agent_id} - {name}")
return agent
def get_agent(self, agent_id: str) -> Optional[Agent]:
"""Get agent by ID"""
return self._agents.get(agent_id)
def list_agents(self) -> List[Agent]:
"""List all agents"""
return list(self._agents.values())
def delete_agent(self, agent_id: str) -> bool:
"""Delete agent by ID"""
if agent_id in self._agents:
del self._agents[agent_id]
logger.info(f"Deleted agent: {agent_id}")
return True
return False
def update_agent(
self,
agent_id: str,
name: str = None,
description: str = None,
instructions: str = None,
tools: List[str] = None
) -> Optional[Agent]:
"""Update agent configuration"""
agent = self._agents.get(agent_id)
if not agent:
return None
if name is not None:
agent.name = name
if description is not None:
agent.description = description
if instructions is not None:
agent.instructions = instructions
if tools is not None:
agent.tools = tools
agent.updated_at = datetime.now()
return agent
# Global service instances
agent_service = AgentService()

185
luxx/agent/task.py Normal file
View File

@ -0,0 +1,185 @@
from dataclasses import dataclass, field
from datetime import datetime
from enum import Enum
from typing import List, Optional, Dict, Any
from luxx.agent.agent import agent_service
from luxx.utils.helpers import generate_id
class TaskStatus(Enum):
"""Task status enum"""
PENDING = "pending"
READY = "ready"
RUNNING = "running"
BLOCK = "block"
TERMINATED = "terminated"
class StepStatus(Enum):
"""Step status enum"""
PENDING = "pending"
RUNNING = "running"
COMPLETED = "completed"
FAILED = "failed"
SKIPPED = "skipped"
@dataclass
class Step:
"""Task step"""
id: str
name: str
description: str = ""
status: StepStatus = StepStatus.PENDING
result: Optional[Dict[str, Any]] = None
created_at: datetime = field(default_factory=datetime.now)
updated_at: datetime = field(default_factory=datetime.now)
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary"""
return {
"id": self.id,
"name": self.name,
"description": self.description,
"status": self.status.value,
"result": self.result,
"created_at": self.created_at.isoformat() if self.created_at else None,
"updated_at": self.updated_at.isoformat() if self.updated_at else None
}
@dataclass
class Task:
"""Task entity"""
id: str
name: str
description: str = ""
goal: str = ""
status: TaskStatus = TaskStatus.PENDING
steps: List[Step] = field(default_factory=list)
subtasks: List["Task"] = field(default_factory=list)
result: Optional[Dict[str, Any]] = None
created_at: datetime = field(default_factory=datetime.now)
updated_at: datetime = field(default_factory=datetime.now)
def to_dict(self) -> Dict[str, Any]:
"""Convert to dictionary"""
return {
"id": self.id,
"name": self.name,
"description": self.description,
"goal": self.goal,
"status": self.status.value,
"steps": [s.to_dict() for s in self.steps],
"result": self.result,
"created_at": self.created_at.isoformat() if self.created_at else None,
"updated_at": self.updated_at.isoformat() if self.updated_at else None
}
class TaskService:
"""Task service for managing tasks"""
def __init__(self):
self._tasks: Dict[str, Task] = {}
def create_task(
self,
agent_id: str,
name: str,
goal: str,
description: str = "",
steps: List[Dict[str, Any]] = None
) -> Optional[Task]:
"""Create task for agent, optionally as subtask"""
agent = agent_service.get_agent(agent_id)
if not agent:
return None
task_id = generate_id("task")
task = Task(
id=task_id,
name=name,
description=description,
goal=goal
)
# Add steps
if steps:
for step_data in steps:
step = Step(
id=generate_id("step"),
name=step_data.get("name", ""),
description=step_data.get("description", "")
)
task.steps.append(step)
agent.current_task = task
self._tasks[task_id] = task
self._logger.info(f"Created task: {task_id} for agent: {agent_id}")
return task
def get_task(self, task_id: str) -> Optional[Task]:
"""Get task by ID"""
return self._tasks.get(task_id)
def list_tasks(self, agent_id: str = None) -> List[Task]:
"""List tasks, optionally filtered by agent"""
if agent_id:
agent = self._agent_service.get_agent(agent_id)
if agent and agent.current_task:
return [agent.current_task]
return []
return list(self._tasks.values())
def update_task_status(
self,
task_id: str,
status: TaskStatus,
result: Any = None
) -> Optional[Task]:
"""Update task status"""
task = self._tasks.get(task_id)
if not task:
return None
task.status = status
task.result = result
task.updated_at = datetime.now()
return task
def add_steps(
self,
task_id: str,
steps: List[Dict[str, Any]]
) -> Optional[List[Step]]:
"""Add steps to task"""
task = self._tasks.get(task_id)
if not task:
return None
result = []
for step_data in steps:
step = Step(
id=generate_id("step"),
name=step_data.get("name", ""),
description=step_data.get("description", "")
)
task.steps.append(step)
result.append(step)
task.updated_at = datetime.now()
return result
def delete_task(self, task_id: str) -> bool:
"""Delete task"""
if task_id not in self._tasks:
return False
del self._tasks[task_id]
return True
task_service = TaskService()

311
luxx/routes/agent.py Normal file
View File

@ -0,0 +1,311 @@
"""Agent routes module"""
from typing import List, Optional
from fastapi import APIRouter
from pydantic import BaseModel
from luxx.agent.agent import agent_service, task_service
from luxx.agent.task import TaskStatus, StepStatus
from luxx.utils.helpers import success_response, error_response
router = APIRouter(prefix="/agent", tags=["Agent"])
# ==================== Request Models ====================
class CreateAgentRequest(BaseModel):
"""Create agent request"""
name: str
description: str = ""
instructions: str = ""
tools: List[str] = []
class UpdateAgentRequest(BaseModel):
"""Update agent request"""
name: Optional[str] = None
description: Optional[str] = None
instructions: Optional[str] = None
tools: Optional[List[str]] = None
class TaskStep(BaseModel):
"""Task step"""
name: str
description: str = ""
class CreateTaskRequest(BaseModel):
"""Create task request"""
name: str
goal: str
description: str = ""
steps: List[TaskStep] = []
class CreateSubtaskRequest(BaseModel):
"""Create subtask request"""
name: str
goal: str
description: str = ""
class UpdateTaskStatusRequest(BaseModel):
"""Update task status request"""
status: str
# ==================== Agent Endpoints ====================
@router.post("/agents", response_model=dict)
def create_agent(request: CreateAgentRequest):
"""Create a new agent instance"""
agent = agent_service.create_agent(
name=request.name,
description=request.description,
instructions=request.instructions,
tools=request.tools
)
return success_response(
data=agent.to_dict(),
message="Agent created successfully"
)
@router.get("/agents", response_model=dict)
def list_agents():
"""List all agents"""
agents = agent_service.list_agents()
return success_response(
data={
"agents": [a.to_dict() for a in agents],
"total": len(agents)
}
)
@router.get("/agents/{agent_id}", response_model=dict)
def get_agent(agent_id: str):
"""Get agent details"""
agent = agent_service.get_agent(agent_id)
if not agent:
return error_response("Agent not found", 404)
return success_response(data=agent.to_dict())
@router.put("/agents/{agent_id}", response_model=dict)
def update_agent(agent_id: str, request: UpdateAgentRequest):
"""Update agent configuration"""
agent = agent_service.update_agent(
agent_id=agent_id,
name=request.name,
description=request.description,
instructions=request.instructions,
tools=request.tools
)
if not agent:
return error_response("Agent not found", 404)
return success_response(
data=agent.to_dict(),
message="Agent updated successfully"
)
@router.delete("/agents/{agent_id}", response_model=dict)
def delete_agent(agent_id: str):
"""Delete agent instance"""
if agent_service.delete_agent(agent_id):
return success_response(message="Agent deleted successfully")
return error_response("Agent not found", 404)
# ==================== Task Endpoints ====================
@router.post("/agents/{agent_id}/tasks", response_model=dict)
def create_task(agent_id: str, request: CreateTaskRequest):
"""Create task for agent"""
task = task_service.create_task(
agent_id=agent_id,
name=request.name,
goal=request.goal,
description=request.description,
steps=[s.dict() for s in request.steps] if request.steps else None
)
if not task:
return error_response("Agent not found", 404)
return success_response(
data=task.to_dict(),
message="Task created successfully"
)
@router.get("/agents/{agent_id}/tasks", response_model=dict)
def get_current_task(agent_id: str):
"""Get agent's current task"""
tasks = task_service.list_tasks(agent_id=agent_id)
if not tasks:
return success_response(
data={"task": None},
message="No task found"
)
return success_response(data={"task": tasks[0].to_dict()})
@router.get("/tasks", response_model=dict)
def list_tasks(agent_id: str = None):
"""List all tasks, optionally filtered by agent"""
tasks = task_service.list_tasks(agent_id=agent_id)
return success_response(
data={
"tasks": [t.to_dict() for t in tasks],
"total": len(tasks)
}
)
@router.get("/tasks/{task_id}", response_model=dict)
def get_task(task_id: str):
"""Get task details"""
task = task_service.get_task(task_id)
if not task:
return error_response("Task not found", 404)
return success_response(data=task.to_dict())
@router.put("/tasks/{task_id}/status", response_model=dict)
def update_task_status(task_id: str, request: UpdateTaskStatusRequest):
"""Update task status"""
try:
task_status = TaskStatus(request.status)
except ValueError:
valid_statuses = [s.value for s in TaskStatus]
return error_response(f"Invalid status: {request.status}. Valid: {valid_statuses}", 400)
task = task_service.update_task_status(task_id, task_status)
if not task:
return error_response("Task not found", 404)
return success_response(
data=task.to_dict(),
message="Task status updated"
)
@router.delete("/tasks/{task_id}", response_model=dict)
def delete_task(task_id: str):
"""Delete task"""
if task_service.delete_task(task_id):
return success_response(message="Task deleted successfully")
return error_response("Task not found", 404)
# ==================== Step Endpoints ====================
@router.post("/tasks/{task_id}/steps", response_model=dict)
def add_step(task_id: str, name: str, description: str = ""):
"""Add step to task"""
step = task_service.add_step(task_id, name, description)
if not step:
return error_response("Task not found", 404)
return success_response(
data=step.to_dict(),
message="Step added successfully"
)
@router.put("/tasks/{task_id}/steps/{step_id}/status", response_model=dict)
def update_step_status(task_id: str, step_id: str, status: str):
"""Update step status"""
try:
step_status = StepStatus(status)
except ValueError:
valid_statuses = [s.value for s in StepStatus]
return error_response(f"Invalid status: {status}. Valid: {valid_statuses}", 400)
task = task_service.get_task(task_id)
if not task:
return error_response("Task not found", 404)
for step in task.steps:
if step.id == step_id:
step.status = step_status
step.updated_at = __import__("datetime").datetime.now()
return success_response(
data=step.to_dict(),
message="Step status updated"
)
return error_response("Step not found", 404)
# ==================== Subtask Endpoints ====================
@router.post("/tasks/{task_id}/subtasks", response_model=dict)
def create_subtask(task_id: str, request: CreateSubtaskRequest):
"""Add subtask to parent task"""
subtask = task_service.add_subtask(
parent_task_id=task_id,
name=request.name,
goal=request.goal,
description=request.description
)
if not subtask:
return error_response("Parent task not found", 404)
return success_response(
data=subtask.to_dict(),
message="Subtask created successfully"
)
@router.get("/tasks/{task_id}/subtasks", response_model=dict)
def list_subtasks(task_id: str):
"""List subtasks of a task"""
task = task_service.get_task(task_id)
if not task:
return error_response("Task not found", 404)
return success_response(
data={
"subtasks": [t.to_dict() for t in task.subtasks],
"total": len(task.subtasks)
}
)
# ==================== Execution Endpoints ====================
@router.post("/agents/{agent_id}/execute", response_model=dict)
def execute_agent(agent_id: str, goal: str):
"""Trigger agent to execute a goal"""
agent = agent_service.get_agent(agent_id)
if not agent:
return error_response("Agent not found", 404)
if agent.status == "executing":
return error_response("Agent is already executing", 400)
# Update agent status
agent.status = "executing"
agent.updated_at = __import__("datetime").datetime.now()
# Create task
task = task_service.create_task(
agent_id=agent_id,
name=f"Task: {goal[:50]}...",
goal=goal,
description=f"Auto-generated task for goal: {goal}"
)
if not task:
return error_response("Failed to create task", 500)
return success_response(
data={
"agent_id": agent_id,
"task_id": task.id,
"status": "executing",
"message": "Agent execution started"
},
message="Execution started"
)

View File

@ -2,17 +2,17 @@
import json import json
import uuid import uuid
import logging import logging
from typing import List, Dict, Any, AsyncGenerator, Optional
from luxx.models import Conversation, Message from typing import List, Dict,AsyncGenerator
from luxx.models import Conversation, Message, LLMProvider
from luxx.tools.executor import ToolExecutor from luxx.tools.executor import ToolExecutor
from luxx.tools.core import registry from luxx.tools.core import registry
from luxx.services.llm_client import LLMClient from luxx.services.llm_client import LLMClient
from luxx.config import config from luxx.database import SessionLocal
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# Maximum iterations to prevent infinite loops # Maximum iterations to prevent infinite loops
MAX_ITERATIONS = 10 MAX_ITERATIONS = 20
def _sse_event(event: str, data: dict) -> str: def _sse_event(event: str, data: dict) -> str:
@ -24,8 +24,6 @@ def get_llm_client(conversation: Conversation = None):
"""Get LLM client, optionally using conversation's provider. Returns (client, max_tokens)""" """Get LLM client, optionally using conversation's provider. Returns (client, max_tokens)"""
max_tokens = None max_tokens = None
if conversation and conversation.provider_id: if conversation and conversation.provider_id:
from luxx.models import LLMProvider
from luxx.database import SessionLocal
db = SessionLocal() db = SessionLocal()
try: try:
provider = db.query(LLMProvider).filter(LLMProvider.id == conversation.provider_id).first() provider = db.query(LLMProvider).filter(LLMProvider.id == conversation.provider_id).first()
@ -57,8 +55,6 @@ class ChatService:
include_system: bool = True include_system: bool = True
) -> List[Dict[str, str]]: ) -> List[Dict[str, str]]:
"""Build message list""" """Build message list"""
from luxx.database import SessionLocal
from luxx.models import Message
messages = [] messages = []
@ -148,7 +144,7 @@ class ChatService:
text_step_id = None text_step_id = None
text_step_idx = None text_step_idx = None
for iteration in range(MAX_ITERATIONS): for _ in range(MAX_ITERATIONS):
# Stream from LLM # Stream from LLM
full_content = "" full_content = ""
full_thinking = "" full_thinking = ""
@ -381,9 +377,8 @@ class ChatService:
# No tool calls - final iteration, save message # No tool calls - final iteration, save message
msg_id = str(uuid.uuid4()) msg_id = str(uuid.uuid4())
# 使用 API 返回的真实 completion_tokens如果 API 没返回则降级使用估算值 actual_token_count = total_usage.get("completion_tokens", 0)
actual_token_count = total_usage.get("completion_tokens", 0) or len(full_content) // 4 logger.info(f"total_usage: {total_usage}")
logger.info(f"[TOKEN] total_usage: {total_usage}, actual_token_count: {actual_token_count}")
self._save_message( self._save_message(
conversation.id, conversation.id,
@ -433,8 +428,7 @@ class ChatService:
usage: dict = None usage: dict = None
): ):
"""Save the assistant message to database.""" """Save the assistant message to database."""
from luxx.database import SessionLocal
from luxx.models import Message
content_json = { content_json = {
"text": full_content, "text": full_content,