1"""

2 AgentOS v0.30 FastAPI REST服务 — 暴露Agent能力。

3"""

4

5 from fastapi import FastAPI, HTTPException

6 from fastapi.middleware.cors import CORSMiddleware

7 from pydantic import BaseModel, Field

8 from typing import Optional

9 import asyncio

10 import uuid

11

12 from agentos.core.loop import AgentLoop, AgentResult, LoopState

13

14

15 class RunRequest(BaseModel):

16 """Agent 运行请求体。"""

17

18 task: str

19 session_id: str = ""

20 stream: bool = False

21 max_iterations: int = 100

22 model: str = "auto"

23

24

25 class RunResponse(BaseModel):

26 """Agent 运行响应体。"""

27

28 session_id: str

29 output: str

30 iterations: int

31 cost_usd: float

32 tokens_used: dict

33 duration_ms: float

34 state: str

35 error: str | None = None

36

37

38 class AgentAPI:

39 """AgentOS REST API 服务。"""

40

41 def __init__(self, loop: AgentLoop):

42 self.loop = loop

43 self.app = FastAPI(title="AgentOS v0.30", version="0.30.0")

44 self._setup_middleware()

45 self._setup_routes()

46

47 def _setup_middleware(self):

48 self.app.add_middleware(

49 CORSMiddleware,

50 allow_origins=["*"],

51 allow_credentials=True,

52 allow_methods=["*"],

53 allow_headers=["*"],

54 )

55

56 def _setup_routes(self):

57 @self.app.get("/health")

58 async def health():

59 return {"status": "ok", "version": "0.30.0"}

60

61 @self.app.post("/run", response_model=RunResponse)

62 async def run_task(req: RunRequest):

63 sid = req.session_id or str(uuid.uuid4())[:8]

64 result = await self.loop.run(task=req.task, session_id=sid)

65 return RunResponse(

66 session_id=sid,

67 output=result.output,

68 iterations=result.iterations,

69 cost_usd=result.cost_usd,

70 tokens_used=result.tokens_used,

71 duration_ms=result.duration_ms,

72 state=result.final_state.value,

73 error=result.error,

74 )

75

76 @self.app.post("/cancel/{session_id}")

77 async def cancel(session_id: str):

78 self.loop.cancel()

79 return {"cancelled": True}

80

81 @self.app.get("/sessions/{session_id}")

82 async def get_session(session_id: str):

83 ctx = self.loop.context_manager

84 return {

85 "session_id": session_id,

86 "task": ctx.current_task,

87 "step_count": ctx.step_count,

88 "messages": len(ctx._messages),

89 }

90

91 @self.app.get("/costs")

92 async def get_costs():

93 return {

94 "total_cost": self.loop.cost_tracker.total_cost,

95 "total_tokens": self.loop.cost_tracker.total_tokens,

96 "by_model": self.loop.cost_tracker.cost_by_model(),

97 "records_count": len(self.loop.cost_tracker.records),

98 }

99

100 @self.app.get("/tools")

101 async def list_tools():

102 return {

103 "count": len(self.loop.tool_registry._tools),

104 "tools": [

105 {"name": t.name, "description": t.description, "is_read": t.is_read, "category": t.category}

106 for t in self.loop.tool_registry._tools.values()

107 ],

108 }

109

110 def serve(self, host: str = "0.0.0.0", port: int = 8080):

111 import uvicorn

112 uvicorn.run(self.app, host=host, port=port)