docs: 在README中新增项目讲解文档

This commit is contained in:
peakxy
2026-06-02 15:55:39 +08:00
parent 60e7777cbd
commit dd9381ad1d
2 changed files with 656 additions and 189 deletions

561
README.md
View File

@@ -2,41 +2,45 @@
[![Go](https://img.shields.io/badge/Go-1.25.6+-00ADD8?style=flat-square&logo=go)](https://go.dev/) [![Go](https://img.shields.io/badge/Go-1.25.6+-00ADD8?style=flat-square&logo=go)](https://go.dev/)
[![Gin](https://img.shields.io/badge/Gin-HTTP-00ACD7?style=flat-square)](https://gin-gonic.com/) [![Gin](https://img.shields.io/badge/Gin-HTTP-00ACD7?style=flat-square)](https://gin-gonic.com/)
[![Google ADK Go](https://img.shields.io/badge/Google%20ADK-Go-4285F4?style=flat-square)](https://google.github.io/adk-docs/)
[![Eino](https://img.shields.io/badge/Eino-Agent-111827?style=flat-square)](https://github.com/cloudwego/eino) [![Eino](https://img.shields.io/badge/Eino-Agent-111827?style=flat-square)](https://github.com/cloudwego/eino)
[![ADK Go](https://img.shields.io/badge/Google%20ADK-Go-4285F4?style=flat-square)](https://github.com/google/adk-go)
[![GORM](https://img.shields.io/badge/GORM-MySQL-2D3748?style=flat-square)](https://gorm.io/) [![GORM](https://img.shields.io/badge/GORM-MySQL-2D3748?style=flat-square)](https://gorm.io/)
[![Redis](https://img.shields.io/badge/Redis-Session-DC382D?style=flat-square&logo=redis)](https://redis.io/) [![Redis](https://img.shields.io/badge/Redis-Optional-DC382D?style=flat-square&logo=redis)](https://redis.io/)
[![Next.js](https://img.shields.io/badge/Next.js-Frontend-000000?style=flat-square&logo=nextdotjs)](https://nextjs.org/) [![Next.js](https://img.shields.io/badge/Next.js-Demo%20UI-000000?style=flat-square&logo=nextdotjs)](https://nextjs.org/)
AI Agent Scaffold Go 是一个面向 Agent 应用开发的 Go 脚手架,围绕 Gin、Eino、Google ADK Go、GORM、MySQL、Redis 构建,重点提供清晰的 DDD 分层、配置驱动的 Agent 装配、多 Agent 工作流编排和 HTTP 运行时接口。仓库同时附带一个基于 Next.js 的 `frontend/` 子项目,作为对接后端 API 的示例前端这个场景是通过drawio组件搭建的旨在通过多轮对话来画出理想中的流程图。 AI Agent Scaffold Go 是一个面向 Agent 应用开发的 Go 脚手架,围绕 Gin、Eino、Google ADK Go、GORM、MySQL、Redis 构建,重点提供清晰的 DDD 分层、配置驱动的 Agent 装配、多 Agent 工作流编排和 HTTP 运行时接口。仓库同时附带一个基于 Next.js 的 `frontend/` 子项目,作为对接后端 API 的示例前端这个场景是通过drawio组件搭建的旨在通过多轮对话来画出理想中的流程图。
这个项目适合作为 Agent 平台、智能助手后端、工作流 Agent 服务的起点。它把模型、工具、技能、Agent、Workflow、Runner 的装配逻辑收敛在领域层,通过端口隔离基础设施实现,让核心业务代码不直接依赖具体 SDK 或框架。
## 目录 ## 目录
- [核心特性](#核心特性) - [核心能力](#核心能力)
- [技术栈](#技术栈) - [技术栈](#技术栈)
- [项目结构](#项目结构) - [项目结构](#项目结构)
- [架构设计](#架构设计) - [后端架构](#后端架构)
- [启动流程](#启动流程)
- [Armory 装配流程](#armory-装配流程) - [Armory 装配流程](#armory-装配流程)
- [运行时对话流程](#运行时对话流程)
- [快速开始](#快速开始) - [快速开始](#快速开始)
- [配置说明](#配置说明) - [配置说明](#配置说明)
- [HTTP API](#http-api) - [HTTP API](#http-api)
- [开发命令](#开发命令) - [前端启动](#前端启动)
- [当前实现状态](#当前实现状态) - [开发与验证](#开发与验证)
- [路线图](#路线图) - [联系作者](#联系作者)
- [Star 趋势](#star-趋势)
- [许可证](#许可证)
## 核心特性 ## 项目讲解文档
- **配置驱动 Agent**:通过 YAML 定义模型 API、ChatModel、工具、技能、单 Agent、Workflow 和 Runner。 - 地址:[项目文档](https://my.feishu.cn/wiki/FbISwBnZPiGx0vkxyHFc8a1enig?fromScene=spaceOverview)
- **多 Agent 工作流**:支持 `loop``parallel``sequential` 三类工作流 Agent 组装。 - 内容:简历写法、项目亮点与难点解析等
- **Armory 规则树装配**:使用 `Root -> AiApi -> ChatModel -> Agent -> AgentWorkflow -> Runner` 的显式节点链路构建运行时 Agent。
- **DDD 六层结构**:将 API 契约、应用启动、领域模型、触发器、基础设施和通用类型分离。 ## 核心能力
- **端口隔离基础设施**:领域层依赖本地 ports不直接耦合 Gin、GORM、Redis、Eino、ADK Go 等实现细节。
- **HTTP 运行时接口**:提供查询 Agent、创建会话POST/GET、同步对话、流式对话四类接口 - **配置驱动 Agent**:通过 `configs/agent/*.yaml` 声明模型 API、ChatModel、MCP 工具、skills、LLM Agent、workflow Agent 和 Runner
- **本地开发资产**包含示例配置、MySQL/Redis Docker Compose、环境变量模板和可一键启动的 Next.js 示例前端 - **Armory 装配链**:按 `Root -> AiAPI -> ChatModel -> Agent -> AgentWorkflow -> Runner` 的顺序装配运行时对象
- **多 Agent 工作流**:支持 `loop``parallel``sequential` 三类 workflow Agent串行工作流可通过 `output-key` 把上一步结果注入下一步指令。
- **HTTP 运行时接口**:提供 Agent 列表、创建会话、同步聊天、SSE 流式聊天接口。
- **OpenAI-compatible 模型调用**:基础设施层直接构造 Chat Completions 请求,支持普通响应和 `stream: true`
- **MCP SSE 工具调用**:装配阶段注册 SSE MCP 工具,运行时在模型触发 tool call 后执行 JSON-RPC `tools/call`,再把工具结果回灌给模型。
- **Skill 资源扫描**:支持扫描 `SKILL.md`,把 skill 元数据转换为模型可见工具。
- **分层边界清晰**:领域层依赖本地 `ports`,避免直接耦合 Gin、GORM、Redis、模型 provider 和 ADK 实现。
## 技术栈 ## 技术栈
@@ -44,194 +48,262 @@ AI Agent Scaffold Go 是一个面向 Agent 应用开发的 Go 脚手架,围绕
| --- | --- | | --- | --- |
| 后端语言 | Go 1.25.6+ | | 后端语言 | Go 1.25.6+ |
| HTTP 框架 | Gin | | HTTP 框架 | Gin |
| Agent / 模型封装 | Eino | | Agent 运行与插件 | Google ADK Go |
| Agent 编排 | Google ADK Go | | 模型/工具抽象依赖 | Eino 相关依赖已纳入模块;当前模型调用由本地 OpenAI-compatible adapter 完成 |
| 持久化 | GORM + MySQL | | 配置 | YAML + dotenv + 环境变量占位符 |
| 缓存 / 会话 | Redis | | 模型协议 | OpenAI-compatible Chat Completions |
| 配置 | YAML + 环境变量 | | 工具协议 | MCP SSEstdio/local 配置结构已存在 |
| 日志 | zap | | 持久化适配器 | GORM + MySQL当前默认启动路径未强制启用 |
| 前端 | Next.js + React + TailwindCSS | | 缓存适配器 | go-redis当前默认启动路径未强制启用 |
| 日志 | zap + Gin logger |
| 前端 | Next.js 16 + React 19 + Tailwind CSS + react-drawio |
## 项目结构 ## 项目结构
```text ```text
ai-agent-scaffold-go ai-agent-scaffold-go
├── cmd/server # 服务入口,当前负责启动日志初始化 ├── cmd/server
│ └── main.go # 后端入口:加载 .env、读取配置、构建应用、启动 HTTP 服务
├── configs ├── configs
│ ├── application.yaml # 应用、服务端口、MySQL、Redis、Agent 配置入口 │ ├── application.yaml # 应用、端口、LLM 超时、Agent 配置路径、可选 DB/Redis 设置
│ └── agent │ └── agent
│ ├── only-one-agent.yaml # 单 Agent 示例配置(含 MCP 与 skills │ ├── only-one-agent.yaml # 单 Agent 示例
│ ├── agent-draw-io.yaml # draw.io Agent 示例配置 │ ├── agent-draw-io.yaml # draw.io Agent 工作流示例
│ └── skills/ # 内置 skill 资源battle-plan、pdf 等) │ └── skills/ # skill 资源,按 SKILL.md 扫描
├── deployments ├── deployments
── docker-compose.yml # 本地 MySQL / Redis ── docker-compose.yml # 本地 MySQL/Redis 参考服务
├── frontend # Next.js 示例前端,调用后端 /api/v1 │ ├── mysql/my.cnf
│ └── redis/redis.conf
├── frontend # Next.js draw.io 示例前端
├── internal ├── internal
│ ├── api # DTO 与统一响应封装 │ ├── api
├── app # 应用装配与配置加载边界 │ ├── dto # HTTP 请求/响应 DTO
│ │ └── response # code/info/data 统一响应封装
│ ├── app
│ │ ├── bootstrap # 应用装配配置表加载、Armory、ChatService、Gin 路由
│ │ └── config # application.yaml 与 Agent YAML 加载/校验
│ ├── domain │ ├── domain
│ │ ├── agent │ │ ├── agent
│ │ │ ├── model # Agent 配置、聊天命令、Runner 等领域模型 │ │ │ ├── model # Agent 配置模型、聊天命令、Runner 接口
│ │ │ ├── ports # 模型、工具、Agent、Runner、Registry 等领域端口 │ │ │ ├── ports # 模型、工具、Agent、Runner、Registry、SessionStore 端口
│ │ │ └── service │ │ │ └── service
│ │ │ ├── armory # Agent 装配领域服务与核心节点 │ │ │ ├── armory # Agent 装配领域服务与节点
│ │ │ │ ├── factory # 默认装配工厂 │ │ │ └── chat # 会话解析与聊天运行时服务
│ │ │ │ └── workflow # loop / parallel / sequential 工作流节点
│ │ │ └── chat # 会话与聊天运行时服务
│ │ └── shared/tree # 泛型策略树路由框架 │ │ └── shared/tree # 泛型策略树路由框架
│ ├── infrastructure # Eino、ADK、MySQL、Redis、日志等适配器 │ ├── infrastructure
└── trigger/http # Gin HTTP 入站适配 │ ├── adk # Agent/Runner/插件适配
├── pkg/types # 响应码与应用错误 │ │ ├── ai # OpenAI-compatible client、MCP SSE client、tool/skill 工厂
├── .env.example # 环境变量示例 │ │ ├── cache # Redis client adapter
│ │ ├── logging # zap logger
│ │ └── persistence # MySQL/GORM adapter
│ └── trigger/http # Gin HTTP 入站路由
├── pkg/types # 应用错误码与 AppError
├── .env.example
├── go.mod ├── go.mod
└── README.md └── README.md
``` ```
## 架构设计 ## 后端架构
```mermaid ```mermaid
flowchart LR flowchart LR
Client["客户端 / frontend"] --> Trigger["trigger/http<br/>Gin 路由"] Client["Client / frontend"] --> Gin["trigger/http<br/>Gin routes"]
Trigger --> ChatService["domain/service/chat<br/>聊天服务"] Gin --> ChatService["domain/agent/service/chat"]
ChatService --> Registry["AgentRegistry"] ChatService --> Registry["AgentRegistry<br/>in-memory by default"]
Registry --> Runner["Runner"] ChatService --> SessionStore["SessionStore<br/>in-memory by default"]
Registry --> Runner["model.Runner"]
Runner --> Agent["ADK adapter Agent"]
Agent --> Model["ports.ChatModel"]
Model --> OpenAI["OpenAI-compatible<br/>chat/completions"]
Agent --> ToolRouter["MCPToolRouter"]
ToolRouter --> MCP["MCP SSE tools"]
Config["configs/*.yaml"] --> App["internal/app<br/>配置加载与应用装配"] Config["configs/application.yaml<br/>configs/agent/*.yaml"] --> Bootstrap["internal/app/bootstrap"]
App --> Armory["domain/service/armory<br/>Agent 装配"] Bootstrap --> Armory["domain/agent/service/armory"]
Armory --> Registry Armory --> Registry
Armory --> Ports["domain/agent/ports"]
ChatService --> Ports
Ports --> Infra["internal/infrastructure"]
Infra --> Eino["Eino"]
Infra --> ADK["Google ADK Go"]
Infra --> MySQL["MySQL / GORM"]
Infra --> Redis["Redis"]
``` ```
### 分层边界 后端按端口与适配器组织:
- `internal/api`:请求/响应 DTO 与统一响应结构 - `internal/domain` 定义核心模型、端口、Armory 装配和聊天服务
- `internal/app`:应用启动、配置加载、服务装配边界 - `internal/app/bootstrap` 负责把配置、领域服务、基础设施 adapter 和 Gin 路由连起来
- `internal/domain`领域模型、端口、Armory 装配、聊天运行时、策略树框架 - `internal/infrastructure` 实现外部依赖适配,包括模型 client、MCP client、ADK Runner、Redis、MySQL 和日志
- `internal/trigger`HTTP 等入站触发器 - `internal/trigger/http` 只处理 HTTP 入参、出参和路由注册
- `internal/infrastructure`数据库、缓存、AI SDK、日志等外部依赖适配 - `pkg/types` 提供跨层使用的错误码:`0000` 成功、`0001` 未知错误、`0002` 参数错误、`0003` Agent 不存在
- `pkg/types`:跨层可复用的错误码和应用错误。
领域层保持稳定,不直接导入 Gin、GORM、Redis、Eino、ADK Go 或 provider-specific 的基础设施包。 ## 启动流程
```mermaid
sequenceDiagram
participant Main as cmd/server
participant Config as app/config
participant Bootstrap as app/bootstrap
participant Armory as domain/armory
participant HTTP as Gin
Main->>Main: parse -env and -config flags
Main->>Main: load dotenv file if present
Main->>Config: LoadApplication(configs/application.yaml)
Main->>Bootstrap: Build(context, appCfg, logger)
Bootstrap->>Config: LoadAgentTablesFile(configured paths)
Bootstrap->>Armory: AcceptArmoryAgents(tables)
Armory->>Bootstrap: registered runnable Agents
Bootstrap->>HTTP: create router and register /healthz + /api/v1
Main->>HTTP: engine.Run()
```
入口文件是 `cmd/server/main.go`。默认行为:
- `-env` 默认读取 `.env`,也可通过 `APP_ENV_FILE` 指定;传空字符串可跳过 dotenv。
- `-config` 默认读取 `configs/application.yaml`,也可通过 `APP_CONFIG` 指定。
- dotenv 中的变量会进入进程环境Agent YAML 里的 `${VAR}``${VAR:-default}` 会在加载时展开。
- `configs/application.yaml` 负责声明服务地址、LLM 请求超时、Agent 配置文件路径,以及当前可选的 MySQL/Redis 设置。
- `bootstrap.Build` 会装配 Agent、创建 `ChatService`、注册 Gin 路由并返回可运行的 `Engine`
## Armory 装配流程 ## Armory 装配流程
Armory 是项目里的 Agent 装配链路。它接收 Agent 配置表按节点顺序构建模型、工具、Agent、Workflow 和 Runner Armory 是 Agent 配置到运行时对象的装配链。每个 Agent table 都会使用新的 `DynamicContext`,按节点顺序构建和传递装配状态
```mermaid ```mermaid
flowchart TD flowchart TD
Root["RootNode<br/>装配入口"] --> AiApi["AiAPINode<br/>创建模型 API"] Root["RootNode"] --> Api["AiAPINode<br/>build ModelAPI"]
AiApi --> ChatModel["ChatModelNode<br/>创建 ChatModel 并挂载工具"] Api --> ChatModel["ChatModelNode<br/>build MCP/skill tools<br/>build ChatModel"]
ChatModel --> Agent["AgentNode<br/>创建单 Agent"] ChatModel --> Agent["AgentNode<br/>build LLM Agents"]
Agent --> Workflow["workflow.AgentWorkflowNode<br/>创建工作流 Agent"] Agent --> Workflow["AgentWorkflowNode<br/>build loop/parallel/sequential Agents"]
Workflow --> Runner["RunnerNode<br/>创建并注册 Runner"] Workflow --> Runner["RunnerNode<br/>build Runner and register Agent"]
``` ```
最新代码已经按职责拆分 关键文件
```text - `internal/domain/agent/service/armory/factory/factory.go`:组合完整节点链。
internal/domain/agent/service/armory - `internal/domain/agent/service/armory/context.go`:保存 ModelAPI、ChatModel、已构建 Agent、当前 workflow 进度和临时值。
├── root_node.go - `internal/domain/agent/service/armory/chat_model_node.go`:构建 MCP 工具、skill 工具和 ChatModel。
├── ai_api_node.go - `internal/domain/agent/service/armory/workflow/*.go`:按 `loop``parallel``sequential` 构建 workflow Agent。
├── chat_model_node.go - `internal/domain/agent/service/armory/runner_node.go`:根据 `runner.agent-name` 找到入口 Agent创建 Runner 并注册到 `AgentRegistry`
├── agent_node.go
├── runner_node.go Workflow 行为:
├── factory/factory.go
└── workflow - `sequential`:按配置顺序执行子 Agent并用子 Agent 的 `output-key` 保存中间结果,后续 Agent 指令可用 `{output_key}` 引用。
├── agent_workflow_node.go - `loop`:当前 adapter 中按 fan-out 方式执行配置的子 Agent配置里保留 `max-iterations` 字段。
├── loop_node.go - `parallel`:当前 adapter 中按 fan-out 方式聚合子 Agent 输出。
├── parallel_node.go
└── sequential_node.go ## 运行时对话流程
```mermaid
sequenceDiagram
participant Client
participant Handler as trigger/http
participant Chat as chat.Service
participant Registry as AgentRegistry
participant Runner
participant Agent
participant Model as OpenAI-compatible model
participant Tool as MCPToolRouter
Client->>Handler: POST /api/v1/chat
Handler->>Chat: HandleMessage(agentId,userId,sessionId,message)
Chat->>Registry: Get(agentId)
Chat->>Runner: Run(userId,sessionId,content)
Runner->>Agent: run(content)
Agent->>Model: chat/completions
alt model requests tool calls
Agent->>Tool: CallTool(name,args)
Tool-->>Agent: tool result
Agent->>Model: next chat/completions with tool result
end
Model-->>Agent: final content
Agent-->>Runner: output
Runner-->>Handler: []string
Handler-->>Client: {code,info,data}
``` ```
Workflow 支持三种编排方式: 同步聊天走 `Runner.Run`,流式聊天走 `Runner.Stream`。LLM Agent 最多允许 4 轮 tool-call 循环,避免工具调用无限递归。
- `loop`:循环执行子 Agent支持最大迭代次数配置。
- `parallel`:并行组合多个子 Agent。
- `sequential`:按顺序串联单 Agent 或已装配的 Workflow Agent。
## 快速开始 ## 快速开始
### 环境要求 ### 环境要求
- Go 1.25.6+ - Go 1.25.6+
- Node.js 18+ npm(仅在启动 `frontend/` 时需要 - Node.js 18+ npm,仅启动 `frontend/` 时需要
- Docker可选用于本地 MySQL / Redis - Docker可选仅在本地启动 MySQL/Redis 参考服务时需要
- 一个 OpenAI-compatible Chat Completions 服务
- 如果启用示例里的百度搜索 MCP还需要可访问的 MCP SSE endpoint
### 获取代码并编译 ### 1. 安装依赖并编译
```bash ```bash
git clone <repo-url>
cd ai-agent-scaffold-go cd ai-agent-scaffold-go
go mod tidy go mod tidy
go build ./... go build ./...
``` ```
### 启动后端服务入口 ### 2. 准备环境变量
复制示例文件:
```bash ```bash
go run ./cmd/server cp .env.example .env
``` ```
当前 `cmd/server` 会完成日志初始化并输出启动日志。完整运行时装配、Armory 初始化、Gin 路由挂载等能力已经按包结构准备好,后续可以继续在入口层串接。 至少替换以下变量为自己的真实值:
### 启动本地基础设施 ```dotenv
OPENAI_BASE_URL=https://api.example.com/
OPENAI_API_KEY=replace-with-your-key
BAIDU_SEARCH_MCP_BASE_URI=http://example.com/mcp/
BAIDU_SEARCH_MCP_SSE_ENDPOINT=sse?api_key=replace-with-your-key
```
`configs/agent/*.yaml` 会读取这些变量来构建模型和 MCP 工具。
### 3. 可选:启动本地 MySQL/Redis
当前默认后端启动路径使用内存态 Agent registry 和 session storeMySQL/Redis 适配器已经存在,但不是默认启动的硬依赖。需要本地服务时可以使用:
```bash ```bash
docker compose -f deployments/docker-compose.yml up -d docker compose -f deployments/docker-compose.yml up -d
``` ```
默认端口: 参考端口:
- MySQL`127.0.0.1:13306` - MySQL`127.0.0.1:13306`
- Redis`127.0.0.1:16379` - Redis`127.0.0.1:16379`
### 启动 ### 4. 启动
仓库内置一个 Next.js 示例前端,默认调用后端 `http://localhost:8091/api/v1`。最小启动方式:
```bash ```bash
cd frontend go run ./cmd/server
npm install
npm run dev
``` ```
访问 也可以显式指定文件
```bash
go run ./cmd/server -env .env -config configs/application.yaml
```
默认监听地址来自 `configs/application.yaml`
```text ```text
http://localhost:3000 :8091
``` ```
更多前端使用细节见 `frontend/README.md` 健康检查:
```bash
curl http://localhost:8091/healthz
```
预期响应:
```json
{"status":"ok"}
```
## 配置说明 ## 配置说明
应用配置 ### 应用配置
```text `configs/application.yaml` 控制应用启动参数:
configs/application.yaml
```
Agent 示例配置:
```text
configs/agent/only-one-agent.yaml
configs/agent/agent-draw-io.yaml
```
环境变量示例:
```text
.env.example
```
`configs/application.yaml` 会声明服务端口、本地数据库、Redis 和 Agent 配置路径:
```yaml ```yaml
app: app:
@@ -241,16 +313,30 @@ server:
addr: ":8091" addr: ":8091"
database: database:
required: false required: false
dsn: "root:123456@tcp(127.0.0.1:13306)/ai_agent_scaffold_go?charset=utf8mb4&parseTime=True&loc=Local" dsn: "root:<password>@tcp(127.0.0.1:13306)/ai-agent-scaffold-go?charset=utf8mb4&parseTime=True&loc=Local"
redis: redis:
required: false required: false
addr: "127.0.0.1:16379" addr: "127.0.0.1:16379"
password: ""
db: 0
llm:
request-timeout: 5m
agent: agent:
config-paths: config-paths:
- configs/agent/only-one-agent.yaml - configs/agent/only-one-agent.yaml
- configs/agent/agent-draw-io.yaml
``` ```
Agent 配置示例(节选自 `configs/agent/only-one-agent.yaml` 说明
- `server.addr` 是 Gin 监听地址。
- `llm.request-timeout` 使用 Go `time.ParseDuration` 格式,例如 `30s``5m``1h`
- `agent.config-paths` 可配置多个 Agent YAML启动时会合并所有 tables。
- `database``redis` 设置目前主要对应基础设施 adapter默认 bootstrap 没有强制打开它们。
### Agent 配置
Agent 配置文件结构位于 `configs/agent/*.yaml`
```yaml ```yaml
ai: ai:
@@ -265,17 +351,17 @@ ai:
agent-desc: "single agent demo" agent-desc: "single agent demo"
module: module:
ai-api: ai-api:
base-url: "https://apis.itedus.cn" base-url: ${OPENAI_BASE_URL}
api-key: "${OPENAI_API_KEY}" api-key: ${OPENAI_API_KEY}
completions-path: "v1/chat/completions" completions-path: "v1/chat/completions"
embeddings-path: "v1/embeddings" embeddings-path: "v1/embeddings"
chat-model: chat-model:
model: "gpt-4.1" model: "gpt-5.5"
tool-mcp-list: tool-mcp-list:
- sse: - sse:
name: baidu-search name: baidu-search
base-uri: http://appbuilder.baidu.com base-uri: ${BAIDU_SEARCH_MCP_BASE_URI}
sse-endpoint: /v2/ai_search/mcp/sse?api_key=${BAIDU_SEARCH_MCP_API_KEY} sse-endpoint: ${BAIDU_SEARCH_MCP_SSE_ENDPOINT}
request-timeout: 500000 request-timeout: 500000
tool-skills-list: tool-skills-list:
- type: "resource" - type: "resource"
@@ -292,25 +378,77 @@ ai:
- "myLogPlugin" - "myLogPlugin"
``` ```
连接真实模型服务前,需要复制 `.env.example` 并设置真实的模型 API Key。 启动时会校验:
- `app-name`
- `agent.agent-id`
- `module.ai-api.base-url`
- `module.ai-api.api-key`
- `module.chat-model.model`
- `module.agents[].name`
- `module.agents[].instruction`
- `module.runner.agent-name`
- 每个 MCP tool 必须且只能声明 `local``sse``stdio` 之一
- workflow type 必须是 `loop``parallel``sequential`
默认值:
- `ai-api.completions-path` 默认 `v1/chat/completions`
- `ai-api.embeddings-path` 默认 `v1/embeddings`
- `tool-skills-list[].type` 默认 `directory`
- `agent-workflows[].max-iterations` 默认 `3`
## HTTP API ## HTTP API
基础路径 非流式接口统一返回
```text ```json
/api/v1 {
"code": "0000",
"info": "success",
"data": {}
}
``` ```
这些路由由 `internal/trigger/http/agent_handler.go` 中的 `RegisterAgentRoutes` 注册。当前服务入口还没有把 Gin 路由完整挂到 `cmd/server`,接入时可复用 `RegisterAgentRoutes(router, chatService)` ### GET /healthz
### 查询 Agent 配置 健康检查。
```bash
curl http://localhost:8091/healthz
```
```json
{"status":"ok"}
```
### GET /api/v1/query_ai_agent_config_list
查询已经注册的 Agent。
```bash ```bash
curl http://localhost:8091/api/v1/query_ai_agent_config_list curl http://localhost:8091/api/v1/query_ai_agent_config_list
``` ```
### 创建会话 响应示例:
```json
{
"code": "0000",
"info": "success",
"data": [
{
"agentId": "100003",
"agentName": "single agent",
"agentDesc": "single agent demo"
}
]
}
```
### POST /api/v1/create_session
创建或复用某个 `userId + agentId` 的会话。
```bash ```bash
curl -X POST http://localhost:8091/api/v1/create_session \ curl -X POST http://localhost:8091/api/v1/create_session \
@@ -318,31 +456,107 @@ curl -X POST http://localhost:8091/api/v1/create_session \
-d '{"agentId":"100003","userId":"u1001"}' -d '{"agentId":"100003","userId":"u1001"}'
``` ```
也支持 GET 响应示例
```json
{
"code": "0000",
"info": "success",
"data": {
"sessionId": "testAgent03:u1001:1"
}
}
```
同等 GET 形式:
```bash ```bash
curl 'http://localhost:8091/api/v1/create_session?agentId=100003&userId=u1001' curl 'http://localhost:8091/api/v1/create_session?agentId=100003&userId=u1001'
``` ```
### 同步对话 ### POST /api/v1/chat
同步聊天。`sessionId` 可为空;为空时服务会自动创建或复用会话。
```bash ```bash
curl -X POST http://localhost:8091/api/v1/chat \ curl -X POST http://localhost:8091/api/v1/chat \
-H 'Content-Type: application/json' \ -H 'Content-Type: application/json' \
-d '{"agentId":"100003","userId":"u1001","message":"帮我制定一个学习计划"}' -d '{
"agentId": "100003",
"userId": "u1001",
"sessionId": "",
"message": "帮我制定一个 Go Agent 学习计划"
}'
``` ```
### 流式对话 响应示例:
`/chat_stream` 通过 SSE`text/event-stream`)推送结果,每个分片以 `event: message` 形式发送,错误以 `event: error` 结束。 ```json
{
"code": "0000",
"info": "success",
"data": {
"content": "..."
}
}
```
### POST /api/v1/chat_stream
流式聊天,响应类型为 `text/event-stream`
```bash ```bash
curl -N -X POST http://localhost:8091/api/v1/chat_stream \ curl -N -X POST http://localhost:8091/api/v1/chat_stream \
-H 'Content-Type: application/json' \ -H 'Content-Type: application/json' \
-d '{"agentId":"100003","userId":"u1001","sessionId":"session-u1001","message":"继续"}' -d '{
"agentId": "100003",
"userId": "u1001",
"sessionId": "",
"message": "继续细化第一周计划"
}'
``` ```
## 开发命令 流式片段示例:
```text
event: message
data: 第一周可以从...
event: message
data: 接下来...
```
如果执行出错,服务会发送:
```text
event: error
data: <error message>
```
## 前端启动
`frontend/` 是一个 Next.js draw.io 示例前端,默认调用 `http://localhost:8091/api/v1`;启动方式是 `cd frontend && npm install && npm run dev`,然后访问 `http://localhost:3000`
如需改后端地址,设置:
```bash
NEXT_PUBLIC_API_BASE_URL=http://localhost:8091/api/v1
```
## 开发与验证
后端常用命令:
```bash
go test ./...
go build ./...
```
检查 Go 文件是否需要格式化:
```bash
test -z "$(gofmt -l .)"
```
格式化: 格式化:
@@ -350,45 +564,14 @@ curl -N -X POST http://localhost:8091/api/v1/chat_stream \
gofmt -w . gofmt -w .
``` ```
编译 前端常用命令
```bash ```bash
go build ./... cd frontend
npm run lint
npm run build
``` ```
启动前端开发服务器: ## 联系作者
```bash - 邮箱2465549609@qq.com
cd frontend && npm run dev
```
## 当前实现状态
- DDD 包结构、领域模型、端口、策略树和 Armory 装配链路已经建立。
- Armory 核心节点、工厂、Workflow 节点已按职责拆分。
- ChatService、AgentRegistry、SessionStore 和 HTTP 路由边界已在代码结构中分离。
- Eino、ADK Go、GORM、Redis、zap 等依赖已经纳入模块,并通过本地端口和基础设施包隔离。
- `cmd/server` 已串接配置加载、Armory 装配、Gin 路由和插件链路,启动后即对外提供 `/api/v1` 接口。
- Runner 已接入真实 OpenAI 兼容 ChatModel`/api/v1/chat``chat/completions` 同步接口,`/api/v1/chat_stream``stream: true` SSE 推送 delta。
- SSE MCP 客户端可用:装配阶段连接 `tool-mcp-list[].sse`,运行时模型选中工具后真正发起 JSON-RPC `tools/call` 并把结果回灌给下一轮模型请求,工具调用循环上限为 4 轮。
- Local MCP、stdio MCP 仍返回 unsupported 错误Skill 工具当前只向模型暴露名字,不参与执行。
- `frontend/` 提供基于 Next.js 的 draw.io 示例前端,默认对接后端 `/api/v1`
## 路线图
- 接入更稳定的模型 backoff / 重试与请求级超时控制。
- 补齐 stdio MCP 客户端实现,扩展 local MCP 真实执行入口。
- 让 Skill 工具具备运行时调用能力,并把 skill 元数据注入模型 tool schema。
- 增加基于 MySQL 的 Agent 配置仓储。
- 增加基于 Redis 的分布式会话存储。
- 增加请求追踪、Token 用量、Agent Workflow 事件等可观测能力。
## Star 趋势
[![Star History Chart](https://api.star-history.com/svg?repos=peakxy/ai-agent-scaffold-go&type=Date)](https://star-history.com/#peakxy/ai-agent-scaffold-go&Date)
## 许可证
## 联系方式
- 邮箱: 2465549609@qq.com

284
go.sum Normal file
View File

@@ -0,0 +1,284 @@
cloud.google.com/go v0.123.0 h1:2NAUJwPR47q+E35uaJeYoNhuNEM9kM8SjgRgdeOJUSE=
cloud.google.com/go v0.123.0/go.mod h1:xBoMV08QcqUGuPW65Qfm1o9Y4zKZBpGS+7bImXLTAZU=
cloud.google.com/go/auth v0.20.0 h1:kXTssoVb4azsVDoUiF8KvxAqrsQcQtB53DcSgta74CA=
cloud.google.com/go/auth v0.20.0/go.mod h1:942/yi/itH1SsmpyrbnTMDgGfdy2BUqIKyd0cyYLc5Q=
cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10=
filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA=
filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4=
github.com/airbrake/gobrake v3.6.1+incompatible/go.mod h1:wM4gu3Cn0W0K7GUuVWnlXZU11AGBXMILnrdOU8Kn00o=
github.com/bahlo/generic-list-go v0.2.0 h1:5sz/EEAK+ls5wF+NeqDpk5+iNdMDXrh3z3nPnH1Wvgk=
github.com/bahlo/generic-list-go v0.2.0/go.mod h1:2KvAjgMlE5NNynlg/5iLrrCCZ2+5xWbdbCW3pNTGyYg=
github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA=
github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4=
github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
github.com/buger/jsonparser v1.1.1 h1:2PnMjfWD7wBILjqQbt530v576A/cAbQvEW9gGIpYMUs=
github.com/buger/jsonparser v1.1.1/go.mod h1:6RYKKt7H4d4+iWqouImQ9R2FZql3VbhNgx27UK13J/0=
github.com/bugsnag/bugsnag-go v1.4.0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8=
github.com/bugsnag/panicwrap v1.2.0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE=
github.com/bytedance/gopkg v0.1.3 h1:TPBSwH8RsouGCBcMBktLt1AymVo2TVsBVCY4b6TnZ/M=
github.com/bytedance/gopkg v0.1.3/go.mod h1:576VvJ+eJgyCzdjS+c4+77QF3p7ubbtiKARP3TxducM=
github.com/bytedance/sonic v1.15.0 h1:/PXeWFaR5ElNcVE84U0dOHjiMHQOwNIx3K4ymzh/uSE=
github.com/bytedance/sonic v1.15.0/go.mod h1:tFkWrPz0/CUCLEF4ri4UkHekCIcdnkqXw9VduqpJh0k=
github.com/bytedance/sonic/loader v0.5.0 h1:gXH3KVnatgY7loH5/TkeVyXPfESoqSBSBEiDd5VjlgE=
github.com/bytedance/sonic/loader v0.5.0/go.mod h1:AR4NYCk5DdzZizZ5djGqQ92eEhCCcdf5x77udYiSJRo=
github.com/certifi/gocertifi v0.0.0-20190105021004-abcd57078448/go.mod h1:GJKEexRPVJrBSOjoqN5VNOIKJ5Q3RViH6eu3puDRwx4=
github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
github.com/cloudwego/base64x v0.1.6 h1:t11wG9AECkCDk5fMSoxmufanudBtJ+/HemLstXDLI2M=
github.com/cloudwego/base64x v0.1.6/go.mod h1:OFcloc187FXDaYHvrNIjxSe8ncn0OOM8gEHfghB2IPU=
github.com/cloudwego/eino v0.9.2 h1:q9nsOy79UAs2yiCpVLzEzIOyv1BWbiP1rrdmNcv1wf0=
github.com/cloudwego/eino v0.9.2/go.mod h1:OBD1mrkfkt/pJa4rkg1P0VnaMeOVl7l8IAdEqY//3IQ=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
github.com/eino-contrib/jsonschema v1.0.3 h1:2Kfsm1xlMV0ssY2nuxshS4AwbLFuqmPmzIjLVJ1Fsp0=
github.com/eino-contrib/jsonschema v1.0.3/go.mod h1:cpnX4SyKjWjGC7iN2EbhxaTdLqGjCi0e9DxpLYxddD4=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
github.com/gabriel-vasile/mimetype v1.4.12 h1:e9hWvmLYvtp846tLHam2o++qitpguFiYCKbn0w9jyqw=
github.com/gabriel-vasile/mimetype v1.4.12/go.mod h1:d+9Oxyo1wTzWdyVUPMmXFvp4F9tea18J8ufA774AB3s=
github.com/getsentry/raven-go v0.2.0/go.mod h1:KungGk8q33+aIAZUIVWZDr2OfAEBsO49PX4NzFV5kcQ=
github.com/gin-contrib/sse v1.1.0 h1:n0w2GMuUpWDVp7qSpvze6fAu9iRxJY4Hmj6AmBOU05w=
github.com/gin-contrib/sse v1.1.0/go.mod h1:hxRZ5gVpWMT7Z0B0gSNYqqsSCNIJMjzvm6fqCz9vjwM=
github.com/gin-gonic/gin v1.12.0 h1:b3YAbrZtnf8N//yjKeU2+MQsh2mY5htkZidOM7O0wG8=
github.com/gin-gonic/gin v1.12.0/go.mod h1:VxccKfsSllpKshkBWgVgRniFFAzFb9csfngsqANjnLc=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127 h1:0gkP6mzaMqkmpcJYCFOLkIBwI7xFExG03bbkOkCvUPI=
github.com/go-check/check v0.0.0-20180628173108-788fd7840127/go.mod h1:9ES+weclKsC9YodN5RgxqK/VD9HM9JsCSh7rNhMZE98=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-playground/assert/v2 v2.2.0 h1:JvknZsQTYeFEAhQwI4qEt9cyV5ONwRHC+lYKSsYSR8s=
github.com/go-playground/assert/v2 v2.2.0/go.mod h1:VDjEfimB/XKnb+ZQfWdccd7VUvScMdVu0Titje2rxJ4=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.30.1 h1:f3zDSN/zOma+w6+1Wswgd9fLkdwy06ntQJp0BBvFG0w=
github.com/go-playground/validator/v10 v10.30.1/go.mod h1:oSuBIQzuJxL//3MelwSLD5hc2Tu889bF0Idm9Dg26cM=
github.com/go-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y=
github.com/go-sql-driver/mysql v1.8.1/go.mod h1:wEBSXgmK//2ZFJyE+qWnIsVGmvmEKlqwuVSjsCm7DZg=
github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4=
github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M=
github.com/goccy/go-yaml v1.19.2 h1:PmFC1S6h8ljIz6gMRBopkjP1TVT7xuwrButHID66PoM=
github.com/goccy/go-yaml v1.19.2/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA=
github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM=
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
github.com/google/jsonschema-go v0.4.2 h1:tmrUohrwoLZZS/P3x7ex0WAVknEkBZM46iALbcqoRA8=
github.com/google/jsonschema-go v0.4.2/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
github.com/google/safehtml v0.1.0 h1:EwLKo8qawTKfsi0orxcQAZzu07cICaBeFMegAU9eaT8=
github.com/google/safehtml v0.1.0/go.mod h1:L4KWwDsUJdECRAEpZoBn3O64bQaywRscowZjJAzjHnU=
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
github.com/googleapis/enterprise-certificate-proxy v0.3.15 h1:xolVQTEXusUcAA5UgtyRLjelpFFHWlPQ4XfWGc7MBas=
github.com/googleapis/enterprise-certificate-proxy v0.3.15/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg=
github.com/googleapis/gax-go/v2 v2.22.0 h1:PjIWBpgGIVKGoCXuiCoP64altEJCj3/Ei+kSU5vlZD4=
github.com/googleapis/gax-go/v2 v2.22.0/go.mod h1:irWBbALSr0Sk3qlqb9SyJ1h68WjgeFuiOzI4Rqw5+aY=
github.com/goph/emperror v0.17.2 h1:yLapQcmEsO0ipe9p5TaN22djm3OFV/TfM/fcYP0/J18=
github.com/goph/emperror v0.17.2/go.mod h1:+ZbQ+fUNO/6FNiUo0ujtMjhgad9Xa6fQL9KhH4LNHic=
github.com/gopherjs/gopherjs v1.17.2 h1:fQnZVsXk8uxXIStYb0N4bGk7jeyTalG/wsZjQ25dO0g=
github.com/gopherjs/gopherjs v1.17.2/go.mod h1:pRRIvn/QzFLrKfvEz3qUuEhtE/zLCWfreZ6J5gM2i+k=
github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg=
github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE=
github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU=
github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E=
github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc=
github.com/jinzhu/now v1.1.5 h1:/o9tlHleP7gOFmsnYNz3RGnqzefHA47wQpKrrdTIwXQ=
github.com/jinzhu/now v1.1.5/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8=
github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/jtolds/gls v4.20.0+incompatible h1:xdiiI2gbIgH/gLH7ADydsJ1uDOEzR8yvV7C0MuV77Wo=
github.com/jtolds/gls v4.20.0+incompatible/go.mod h1:QJZ7F/aHp+rZTRtaJ1ow/lLfFfVYBRgL+9YlvaHOwJU=
github.com/kardianos/osext v0.0.0-20190222173326-2bc1f35cddc0/go.mod h1:1NbS8ALrpOvjt0rHPNLyCIeMtbizbir8U//inJ+zuB8=
github.com/klauspost/cpuid/v2 v2.3.0 h1:S4CRMLnYUhGeDFDqkGriYKdfoFlDnMtqTiI/sFzhA9Y=
github.com/klauspost/cpuid/v2 v2.3.0/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/mailru/easyjson v0.7.7 h1:UGYAvKxe3sBsEDzO8ZeWOSlIQfWFlxbzLZe7hwFURr0=
github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc=
github.com/mattn/go-colorable v0.1.2 h1:/bC9yWikZXAL9uJdulbSfyVNIR3n3trXl+v8+1sx8mU=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b h1:j7+1HpAFS1zy5+Q4qx1fWh90gTKwiN4QCGoY9TWyyO4=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg=
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q=
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/nikolalohinski/gonja v1.5.3 h1:GsA+EEaZDZPGJ8JtpeGN78jidhOlxeJROpqMT9fTj9c=
github.com/nikolalohinski/gonja v1.5.3/go.mod h1:RmjwxNiXAEqcq1HeK5SSMmqFJvKOfTfXhkJv6YBtPa4=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/ginkgo v1.8.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
github.com/onsi/gomega v1.5.0/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY=
github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/quic-go/qpack v0.6.0 h1:g7W+BMYynC1LbYLSqRt8PBg5Tgwxn214ZZR34VIOjz8=
github.com/quic-go/qpack v0.6.0/go.mod h1:lUpLKChi8njB4ty2bFLX2x4gzDqXwUpaO1DP9qMDZII=
github.com/quic-go/quic-go v0.59.0 h1:OLJkp1Mlm/aS7dpKgTc6cnpynnD2Xg7C1pwL6vy/SAw=
github.com/quic-go/quic-go v0.59.0/go.mod h1:upnsH4Ju1YkqpLXC305eW3yDZ4NfnNbmQRCMWS58IKU=
github.com/redis/go-redis/v9 v9.20.0 h1:WnQYxLkgO2xiXTCJY0ldIiI8dNqCDlQAG+AtaH7a2a0=
github.com/redis/go-redis/v9 v9.20.0/go.mod h1:v/M13XI1PVCDcm01VtPFOADfZtHf8YW3baQf57KlIkA=
github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
github.com/rollbar/rollbar-go v1.0.2/go.mod h1:AcFs5f0I+c71bpHlXNNDbOWJiKwjFDtISeXco0L5PKQ=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=
github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ=
github.com/slongfield/pyfmt v0.0.0-20220222012616-ea85ff4c361f h1:Z2cODYsUxQPofhpYRMQVwWz4yUVpHF+vPi+eUdruUYI=
github.com/slongfield/pyfmt v0.0.0-20220222012616-ea85ff4c361f/go.mod h1:JqzWyvTuI2X4+9wOHmKSQCYxybB/8j6Ko43qVmXDuZg=
github.com/smarty/assertions v1.15.0 h1:cR//PqUBUiQRakZWqBiFFQ9wb8emQGDb0HeGdqGByCY=
github.com/smarty/assertions v1.15.0/go.mod h1:yABtdzeQs6l1brC900WlRNwj6ZR55d7B+E8C6HtKdec=
github.com/smartystreets/goconvey v1.8.1 h1:qGjIddxOk4grTu9JPOU31tVfq3cNdBlNa5sSznIX1xY=
github.com/smartystreets/goconvey v1.8.1/go.mod h1:+/u4qLyY6x1jReYOp7GOM2FSt8aP9CzCZL03bI28W60=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw=
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=
github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo=
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
github.com/twitchyliquid64/golang-asm v0.15.1 h1:SU5vSMR7hnwNxj24w34ZyCi/FmDZTkS4MhqMhdFk5YI=
github.com/twitchyliquid64/golang-asm v0.15.1/go.mod h1:a1lVb/DtPvCB8fslRZhAngC2+aY1QWCk3Cedj/Gdt08=
github.com/ugorji/go/codec v1.3.1 h1:waO7eEiFDwidsBN6agj1vJQ4AG7lh2yqXyOXqhgQuyY=
github.com/ugorji/go/codec v1.3.1/go.mod h1:pRBVtBSKl77K30Bv8R2P+cLSGaTtex6fsA2Wjqmfxj4=
github.com/wk8/go-ordered-map/v2 v2.1.8 h1:5h/BUHu93oj4gIdvHHHGsScSTMijfx5PeYkE/fJgbpc=
github.com/wk8/go-ordered-map/v2 v2.1.8/go.mod h1:5nJHM5DyteebpVlHnWMV0rPz6Zp7+xBAnxjb1X5vnTw=
github.com/x-cray/logrus-prefixed-formatter v0.5.2 h1:00txxvfBM9muc0jiLIEAkAcIMJzfthRT6usrui8uGmg=
github.com/x-cray/logrus-prefixed-formatter v0.5.2/go.mod h1:2duySbKsL6M18s5GU7VPsoEPHyzalCE06qoARUCeBBE=
github.com/yargevad/filepathx v1.0.0 h1:SYcT+N3tYGi+NvazubCNlvgIPbzAk7i7y2dwg3I5FYc=
github.com/yargevad/filepathx v1.0.0/go.mod h1:BprfX/gpYNJHJfc35GjRRpVcwWXS89gGulUIU5tK3tA=
github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
go.mongodb.org/mongo-driver/v2 v2.5.0 h1:yXUhImUjjAInNcpTcAlPHiT7bIXhshCTL3jVBkF3xaE=
go.mongodb.org/mongo-driver/v2 v2.5.0/go.mod h1:yOI9kBsufol30iFsl1slpdq1I0eHPzybRWdyYUs8K/0=
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY=
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo=
go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
go.opentelemetry.io/otel/log v0.16.0 h1:DeuBPqCi6pQwtCK0pO4fvMB5eBq6sNxEnuTs88pjsN4=
go.opentelemetry.io/otel/log v0.16.0/go.mod h1:rWsmqNVTLIA8UnwYVOItjyEZDbKIkMxdQunsIhpUMes=
go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
go.opentelemetry.io/otel/sdk v1.43.0/go.mod h1:P+IkVU3iWukmiit/Yf9AWvpyRDlUeBaRg6Y+C58QHzg=
go.opentelemetry.io/otel/sdk/log v0.16.0 h1:e/b4bdlQwC5fnGtG3dlXUrNOnP7c8YLVSpSfEBIkTnI=
go.opentelemetry.io/otel/sdk/log v0.16.0/go.mod h1:JKfP3T6ycy7QEuv3Hj8oKDy7KItrEkus8XJE6EoSzw4=
go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
go.opentelemetry.io/otel/sdk/metric v1.43.0/go.mod h1:C/RJtwSEJ5hzTiUz5pXF1kILHStzb9zFlIEe85bhj6A=
go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y=
go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU=
go.uber.org/multierr v1.10.0 h1:S0h4aNzvfcFsC3dRF1jLoaov7oRaKqRGC/pUEJ2yvPQ=
go.uber.org/multierr v1.10.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
go.uber.org/zap v1.28.0 h1:IZzaP1Fv73/T/pBMLk4VutPl36uNC+OSUh3JLG3FIjo=
go.uber.org/zap v1.28.0/go.mod h1:rDLpOi171uODNm/mxFcuYWxDsqWSAVkFdX4XojSKg/Q=
go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
golang.org/x/arch v0.22.0 h1:c/Zle32i5ttqRXjdLyyHZESLD/bB90DCU1g9l/0YBDI=
golang.org/x/arch v0.22.0/go.mod h1:dNHoOeKiyja7GTvF9NJS1l3Z2yntpQNzgrjh1cU103A=
golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
golang.org/x/crypto v0.51.0 h1:IBPXwPfKxY7cWQZ38ZCIRPI50YLeevDLlLnyC5wRGTI=
golang.org/x/crypto v0.51.0/go.mod h1:8AdwkbraGNABw2kOX6YFPs3WM22XqI4EXEd8g+x7Oc8=
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1 h1:MGwJjxBy0HJshjDNfLsYO8xppfqWlA5ZT9OhtUUhTNw=
golang.org/x/exp v0.0.0-20230713183714-613f0c0eb8a1/go.mod h1:FXUEEKJgO7OQYeo8N01OfiKP8RXMtf6e8aTskBGqWdc=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.54.0 h1:2zJIZAxAHV/OHCDTCOHAYehQzLfSXuf/5SoL/Dv6w/w=
golang.org/x/net v0.54.0/go.mod h1:Sj4oj8jK6XmHpBZU/zWHw3BV3abl4Kvi+Ut7cQcY+cQ=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.44.0 h1:ildZl3J4uzeKP07r2F++Op7E9B29JRUy+a27EibtBTQ=
golang.org/x/sys v0.44.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
google.golang.org/adk v1.4.0 h1:Qi4KB9YKD00/I5K9v3QsZ9ng5YiZQ7MfMgM8BZjNcsM=
google.golang.org/adk v1.4.0/go.mod h1:R8tNFnI/eiBXHn7zJPJtqdiK/WXC+tVkyuZsXyNZXN4=
google.golang.org/api v0.279.0 h1:hsx2M2OaRcaKtVYK6vXEUnQvdjnend7ZYES+lYaot74=
google.golang.org/api v0.279.0/go.mod h1:B9TqLBwJqVjp1mtt7WeoQwWRwvu/400y5lETOql+giQ=
google.golang.org/genai v1.57.0 h1:qTyG2ynz5dQy2jF4CvZdLHHVslhR0heMue+zM1a4GNM=
google.golang.org/genai v1.57.0/go.mod h1:A3kkl0nyBjyFlNjgxIwKq70julKbIxpSxqKO5gw/gmk=
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0=
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I=
google.golang.org/genproto/googleapis/api v0.0.0-20260427160629-7cedc36a6bc4 h1:yOzSCGPx+cp5VO7IxvZ9SBFF7j1tZVcNtlHR2iYKtVo=
google.golang.org/genproto/googleapis/api v0.0.0-20260427160629-7cedc36a6bc4/go.mod h1:Q9HWtNeE7tM9npdIsEvqXj1QJIvVoeAV3rtXtS715Cw=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60 h1:seT2EwLWM78plQ7wcDfuWBc/4FAEAXDDiaSol4ku4qo=
google.golang.org/genproto/googleapis/rpc v0.0.0-20260511170946-3700d4141b60/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
google.golang.org/grpc v1.81.0 h1:W3G9N3KQf3BU+YuCtGKJk0CmxQNbAISICD/9AORxLIw=
google.golang.org/grpc v1.81.0/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys=
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw=
gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gorm.io/driver/mysql v1.6.0 h1:eNbLmNTpPpTOVZi8MMxCi2aaIm0ZpInbORNXDwyLGvg=
gorm.io/driver/mysql v1.6.0/go.mod h1:D/oCC2GWK3M/dqoLxnOlaNKmXz8WNTfcS9y5ovaSqKo=
gorm.io/gorm v1.31.1 h1:7CA8FTFz/gRfgqgpeKIBcervUn3xSyPUmr6B2WXJ7kg=
gorm.io/gorm v1.31.1/go.mod h1:XyQVbO2k6YkOis7C2437jSit3SsDK72s7n7rsSHd+Gs=
rsc.io/omap v1.2.0 h1:c1M8jchnHbzmJALzGLclfH3xDWXrPxSUHXzH5C+8Kdw=
rsc.io/omap v1.2.0/go.mod h1:C8pkI0AWexHopQtZX+qiUeJGzvc8HkdgnsWK4/mAa00=
rsc.io/ordered v1.1.1 h1:1kZM6RkTmceJgsFH/8DLQvkCVEYomVDJfBRLT595Uak=
rsc.io/ordered v1.1.1/go.mod h1:evAi8739bWVBRG9aaufsjVc202+6okf8u2QeVL84BCM=