File size: 4,744 Bytes
ae0a268
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
from __future__ import annotations

import json
import os
import time
from pathlib import Path
from typing import Any, Dict

from fastapi import FastAPI, Query
from fastapi.responses import FileResponse, StreamingResponse
from fastapi.staticfiles import StaticFiles

from agent_core import iter_problem_steps


ROOT_DIR = Path(__file__).resolve().parent
STATIC_DIR = ROOT_DIR / "static"
OUTPUT_DIR = ROOT_DIR / "outputs"
AGNES_MODEL = "agnes-2.0-flash"
AGNES_CHAT_COMPLETIONS_URL = "https://apihub.agnes-ai.com/v1/chat/completions"

app = FastAPI(title="General Algorithm Problem Solving Agent")
app.mount("/static", StaticFiles(directory=STATIC_DIR), name="static")

def load_env_file(path: Path) -> None:
    if not path.exists():
        return
    for raw_line in path.read_text(encoding="utf-8", errors="ignore").splitlines():
        line = raw_line.strip()
        if not line or line.startswith("#") or "=" not in line:
            continue
        key, value = line.split("=", 1)
        key = key.strip()
        value = value.strip().strip('"').strip("'")
        if key and key not in os.environ:
            os.environ[key] = value


load_env_file(ROOT_DIR / ".env")


def sse_event(payload: Dict[str, Any]) -> str:
    return f"data: {json.dumps(payload, ensure_ascii=False)}\n\n"


@app.get("/")
def index() -> FileResponse:
    return FileResponse(STATIC_DIR / "index.html")


@app.get("/api/status")
def status() -> Dict[str, Any]:
    api_key = os.getenv("AGNES_API_KEY", "")
    return {
        "enabled": bool(api_key),
        "provider": "Agnes AI",
        "model": AGNES_MODEL,
        "base_url_configured": True,
        "workflow": [
            "Memory",
            "Planner",
            "Retriever",
            "Executor",
            "Script Runner",
            "Evaluator",
            "Loop Controller",
            "Reflector",
            "Artifact Writer",
        ],
    }


@app.get("/api/run")
def run_agent(
    question: str = Query(..., min_length=1),
) -> StreamingResponse:
    def stream():
        selected_key = os.getenv("AGNES_API_KEY", "")

        yield sse_event(
            {
                "type": "status",
                "title": "Agent 启动",
                "content": "已接收问题,开始初始化 Session Memory 与任务状态。",
                "time": time.strftime("%H:%M:%S"),
            }
        )

        try:
            final_state = None
            for index, (item, state) in enumerate(
                iter_problem_steps(
                    question,
                    api_key=selected_key,
                    base_url=AGNES_CHAT_COMPLETIONS_URL,
                    model=AGNES_MODEL,
                ),
                start=1,
            ):
                final_state = state
                yield sse_event(
                    {
                        "type": "step",
                        "index": index,
                        "module": item.get("module", ""),
                        "title": item.get("title", ""),
                        "content": item.get("content", ""),
                        "time": time.strftime("%H:%M:%S"),
                    }
                )
                time.sleep(0.05)

            if final_state is None:
                raise RuntimeError("Agent 未产生任何输出。")

            result = {
                "final_answer": final_state.final_answer,
                "pdf_path": final_state.pdf_path,
                "tex_path": final_state.tex_path,
            }
            yield sse_event(
                {
                    "type": "final",
                    "title": "最终答案",
                    "content": result["final_answer"],
                    "pdf_url": "/api/report/pdf",
                    "tex_url": "/api/report/tex",
                    "time": time.strftime("%H:%M:%S"),
                }
            )
            yield sse_event({"type": "done"})
        except Exception as exc:
            yield sse_event(
                {
                    "type": "error",
                    "title": "运行失败",
                    "content": str(exc),
                    "time": time.strftime("%H:%M:%S"),
                }
            )
            yield sse_event({"type": "done"})

    return StreamingResponse(stream(), media_type="text/event-stream")


@app.get("/api/report/pdf")
def report_pdf() -> FileResponse:
    path = OUTPUT_DIR / "solution_report.pdf"
    return FileResponse(path, filename="solution_report.pdf", media_type="application/pdf")


@app.get("/api/report/tex")
def report_tex() -> FileResponse:
    path = OUTPUT_DIR / "solution_report.tex"
    return FileResponse(path, filename="solution_report.tex", media_type="text/plain")