refactor(task-7): integrate all layers and fix type mismatches
- Merge all branches (domain, infrastructure, repository, provider, adapter, usecase, cli)
- Update ServiceContext to inject dependencies
- Fix Message.Content type mismatch (string vs interface{})
- Update AccountStat to use entity.AccountStat
- Add helper functions for content conversion
This commit is contained in:
parent
8f1b7159ed
commit
7e0b7a970c
|
|
@ -0,0 +1,462 @@
|
||||||
|
# REFACTOR TASKS
|
||||||
|
|
||||||
|
重構任務拆分,支援 git worktree 並行開發。
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task Overview
|
||||||
|
|
||||||
|
### 並行策略
|
||||||
|
|
||||||
|
```
|
||||||
|
時間軸 ──────────────────────────────────────────────────────────────►
|
||||||
|
|
||||||
|
Task 0: Init (必須先完成)
|
||||||
|
│
|
||||||
|
├── Task 1: Domain Layer ─────────────────────────┐
|
||||||
|
│ │
|
||||||
|
│ ┌── Task 2: Infrastructure Layer ────────────┤── 並行
|
||||||
|
│ │ │
|
||||||
|
│ └── Task 3: Repository Layer ────────────────┘
|
||||||
|
│ (依賴 Task 1)
|
||||||
|
│
|
||||||
|
├── Task 4: Provider Layer ──────────────────────┐
|
||||||
|
│ (依賴 Task 1) │
|
||||||
|
│ │── 可並行
|
||||||
|
├── Task 5: Usecase Layer ───────────────────────┤
|
||||||
|
│ (依賴 Task 3) │
|
||||||
|
│ │
|
||||||
|
├── Task 6: Adapter Layer ───────────────────────┘
|
||||||
|
│ (依賴 Task 1)
|
||||||
|
│
|
||||||
|
├── Task 7: Internal Layer ──────────────────────┐
|
||||||
|
│ (整合所有,必須最後) │
|
||||||
|
│ │── 序列
|
||||||
|
├── Task 8: CLI Tools │
|
||||||
|
│ │
|
||||||
|
└── Task 9: Cleanup & Tests ────────────────────┘
|
||||||
|
```
|
||||||
|
|
||||||
|
### Worktree 分支規劃
|
||||||
|
|
||||||
|
| 分支名稱 | 基於 | 任務 | 可並行 |
|
||||||
|
|---------|------|------|--------|
|
||||||
|
| `refactor/init` | `master` | Task 0 | ❌ |
|
||||||
|
| `refactor/domain` | `refactor/init` | Task 1 | ✅ |
|
||||||
|
| `refactor/infrastructure` | `refactor/init` | Task 2 | ✅ |
|
||||||
|
| `refactor/repository` | `refactor/domain` | Task 3 | ✅ |
|
||||||
|
| `refactor/provider` | `refactor/domain` | Task 4 | ✅ |
|
||||||
|
| `refactor/usecase` | `refactor/repository` | Task 5 | ✅ |
|
||||||
|
| `refactor/adapter` | `refactor/domain` | Task 6 | ✅ |
|
||||||
|
| `refactor/internal` | 合併所有 | Task 7 | ❌ |
|
||||||
|
| `refactor/cli` | `refactor/init` | Task 8 | ✅ |
|
||||||
|
| `refactor/cleanup` | 合併所有 | Task 9 | ❌ |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 0: 初始化
|
||||||
|
|
||||||
|
### 分支
|
||||||
|
`refactor/init`
|
||||||
|
|
||||||
|
### 依賴
|
||||||
|
無(必須先完成)
|
||||||
|
|
||||||
|
### 小任務
|
||||||
|
|
||||||
|
- [ ] **0.1** 更新 go.mod (5min)
|
||||||
|
- `go get github.com/zeromicro/go-zero@latest`
|
||||||
|
- `go mod tidy`
|
||||||
|
|
||||||
|
- [ ] **0.2** 建立目錄 (1min)
|
||||||
|
- `mkdir -p api etc`
|
||||||
|
|
||||||
|
- [ ] **0.3** 建立 `api/chat.api` (15min)
|
||||||
|
- 定義 API types
|
||||||
|
- 定義 routes
|
||||||
|
|
||||||
|
- [ ] **0.4** 建立 `etc/chat.yaml` (5min)
|
||||||
|
- 配置參數
|
||||||
|
|
||||||
|
- [ ] **0.5** 更新 Makefile (10min)
|
||||||
|
- 新增 goctl 命令
|
||||||
|
|
||||||
|
- [ ] **0.6** 提交 (2min)
|
||||||
|
|
||||||
|
**預估時間**: ~30min
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 1: Domain Layer
|
||||||
|
|
||||||
|
### 分支
|
||||||
|
`refactor/domain`
|
||||||
|
|
||||||
|
### 依賴
|
||||||
|
Task 0 完成
|
||||||
|
|
||||||
|
### 小任務
|
||||||
|
|
||||||
|
- [ ] **1.1** 建立目錄結構 (1min)
|
||||||
|
- `pkg/domain/entity`
|
||||||
|
- `pkg/domain/repository`
|
||||||
|
- `pkg/domain/usecase`
|
||||||
|
- `pkg/domain/const`
|
||||||
|
|
||||||
|
- [ ] **1.2** `entity/message.go` (10min)
|
||||||
|
- Message, Tool, ToolFunction, ToolCall
|
||||||
|
|
||||||
|
- [ ] **1.3** `entity/chunk.go` (5min)
|
||||||
|
- StreamChunk, ChunkType
|
||||||
|
|
||||||
|
- [ ] **1.4** `entity/account.go` (5min)
|
||||||
|
- Account, AccountStat
|
||||||
|
|
||||||
|
- [ ] **1.5** `repository/account.go` (10min)
|
||||||
|
- AccountPool interface
|
||||||
|
|
||||||
|
- [ ] **1.6** `repository/provider.go` (5min)
|
||||||
|
- Provider interface
|
||||||
|
|
||||||
|
- [ ] **1.7** `usecase/chat.go` (15min)
|
||||||
|
- ChatUsecase interface
|
||||||
|
|
||||||
|
- [ ] **1.8** `usecase/agent.go` (5min)
|
||||||
|
- AgentRunner interface
|
||||||
|
|
||||||
|
- [ ] **1.9** `const/models.go` (10min)
|
||||||
|
- Model 常數
|
||||||
|
|
||||||
|
- [ ] **1.10** `const/errors.go` (5min)
|
||||||
|
- 錯誤定義
|
||||||
|
|
||||||
|
- [ ] **1.11** 提交 (2min)
|
||||||
|
|
||||||
|
**預估時間**: ~2h
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 2: Infrastructure Layer
|
||||||
|
|
||||||
|
### 分支
|
||||||
|
`refactor/infrastructure`
|
||||||
|
|
||||||
|
### 依賴
|
||||||
|
Task 0 完成(可與 Task 1 並行)
|
||||||
|
|
||||||
|
### 小任務
|
||||||
|
|
||||||
|
- [ ] **2.1** 建立目錄 (2min)
|
||||||
|
- `pkg/infrastructure/{process,parser,httputil,logger,env,workspace,winlimit}`
|
||||||
|
|
||||||
|
- [ ] **2.2** 遷移 process (10min)
|
||||||
|
- runner.go, kill_unix.go, kill_windows.go, process_test.go
|
||||||
|
|
||||||
|
- [ ] **2.3** 遷移 parser (5min)
|
||||||
|
- stream.go, stream_test.go
|
||||||
|
|
||||||
|
- [ ] **2.4** 遷移 httputil (5min)
|
||||||
|
- httputil.go, httputil_test.go
|
||||||
|
|
||||||
|
- [ ] **2.5** 遷移 logger (5min)
|
||||||
|
- logger.go
|
||||||
|
|
||||||
|
- [ ] **2.6** 遷移 env (5min)
|
||||||
|
- env.go, env_test.go
|
||||||
|
|
||||||
|
- [ ] **2.7** 遷移 workspace (5min)
|
||||||
|
- workspace.go
|
||||||
|
|
||||||
|
- [ ] **2.8** 遷移 winlimit (5min)
|
||||||
|
- winlimit.go, winlimit_test.go
|
||||||
|
|
||||||
|
- [ ] **2.9** 驗證編譯 (5min)
|
||||||
|
|
||||||
|
- [ ] **2.10** 提交 (2min)
|
||||||
|
|
||||||
|
**預估時間**: ~1h
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 3: Repository Layer
|
||||||
|
|
||||||
|
### 分支
|
||||||
|
`refactor/repository`
|
||||||
|
|
||||||
|
### 依賴
|
||||||
|
Task 1 完成
|
||||||
|
|
||||||
|
### 小任務
|
||||||
|
|
||||||
|
- [ ] **3.1** 建立目錄 (1min)
|
||||||
|
|
||||||
|
- [ ] **3.2** 遷移 account.go (20min)
|
||||||
|
- AccountPool 實作
|
||||||
|
- 移除全局變數
|
||||||
|
|
||||||
|
- [ ] **3.3** 遷移 provider.go (10min)
|
||||||
|
- Provider 工廠
|
||||||
|
|
||||||
|
- [ ] **3.4** 遷移測試 (5min)
|
||||||
|
|
||||||
|
- [ ] **3.5** 驗證編譯 (5min)
|
||||||
|
|
||||||
|
- [ ] **3.6** 提交 (2min)
|
||||||
|
|
||||||
|
**預估時間**: ~1h
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 4: Provider Layer
|
||||||
|
|
||||||
|
### 分支
|
||||||
|
`refactor/provider`
|
||||||
|
|
||||||
|
### 依賴
|
||||||
|
Task 1 完成
|
||||||
|
|
||||||
|
### 小任務
|
||||||
|
|
||||||
|
- [ ] **4.1** 建立目錄 (1min)
|
||||||
|
- `pkg/provider/cursor`
|
||||||
|
- `pkg/provider/geminiweb`
|
||||||
|
|
||||||
|
- [ ] **4.2** 遷移 cursor provider (5min)
|
||||||
|
|
||||||
|
- [ ] **4.3** 遷移 geminiweb provider (10min)
|
||||||
|
|
||||||
|
- [ ] **4.4** 更新 import (5min)
|
||||||
|
|
||||||
|
- [ ] **4.5** 驗證編譯 (5min)
|
||||||
|
|
||||||
|
- [ ] **4.6** 提交 (2min)
|
||||||
|
|
||||||
|
**預估時間**: ~30min
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 5: Usecase Layer
|
||||||
|
|
||||||
|
### 分支
|
||||||
|
`refactor/usecase`
|
||||||
|
|
||||||
|
### 依賴
|
||||||
|
Task 3 完成
|
||||||
|
|
||||||
|
### 小任務
|
||||||
|
|
||||||
|
- [ ] **5.1** 建立目錄 (1min)
|
||||||
|
|
||||||
|
- [ ] **5.2** 建立 chat.go (30min)
|
||||||
|
- 核心聊天邏輯
|
||||||
|
|
||||||
|
- [ ] **5.3** 遷移 agent.go (20min)
|
||||||
|
- runner, token, cmdargs, maxmode
|
||||||
|
|
||||||
|
- [ ] **5.4** 遷移 sanitizer (10min)
|
||||||
|
|
||||||
|
- [ ] **5.5** 遷移 toolcall (10min)
|
||||||
|
|
||||||
|
- [ ] **5.6** 驗證編譯 (5min)
|
||||||
|
|
||||||
|
- [ ] **5.7** 提交 (2min)
|
||||||
|
|
||||||
|
**預估時間**: ~2h
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 6: Adapter Layer
|
||||||
|
|
||||||
|
### 分支
|
||||||
|
`refactor/adapter`
|
||||||
|
|
||||||
|
### 依賴
|
||||||
|
Task 1 完成
|
||||||
|
|
||||||
|
### 小任務
|
||||||
|
|
||||||
|
- [ ] **6.1** 建立目錄 (1min)
|
||||||
|
|
||||||
|
- [ ] **6.2** 遷移 openai adapter (10min)
|
||||||
|
|
||||||
|
- [ ] **6.3** 遷移 anthropic adapter (10min)
|
||||||
|
|
||||||
|
- [ ] **6.4** 更新 import (5min)
|
||||||
|
|
||||||
|
- [ ] **6.5** 驗證編譯 (5min)
|
||||||
|
|
||||||
|
- [ ] **6.6** 提交 (2min)
|
||||||
|
|
||||||
|
**預估時間**: ~30min
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 7: Internal Layer
|
||||||
|
|
||||||
|
### 分支
|
||||||
|
`refactor/internal`
|
||||||
|
|
||||||
|
### 依賴
|
||||||
|
Task 1-6 全部完成
|
||||||
|
|
||||||
|
### 小任務
|
||||||
|
|
||||||
|
- [ ] **7.1** 合併所有分支 (5min)
|
||||||
|
|
||||||
|
- [ ] **7.2** 更新 config/config.go (15min)
|
||||||
|
- 使用 rest.RestConf
|
||||||
|
|
||||||
|
- [ ] **7.3** 建立 svc/servicecontext.go (30min)
|
||||||
|
- DI 容器
|
||||||
|
|
||||||
|
- [ ] **7.4** 建立 logic/ (1h)
|
||||||
|
- chatcompletionlogic.go
|
||||||
|
- geminichatlogic.go
|
||||||
|
- anthropiclogic.go
|
||||||
|
- healthlogic.go
|
||||||
|
- modelslogic.go
|
||||||
|
|
||||||
|
- [ ] **7.5** 建立 handler/ (1h)
|
||||||
|
- 自訂 SSE handler
|
||||||
|
|
||||||
|
- [ ] **7.6** 建立 middleware/ (20min)
|
||||||
|
- auth.go
|
||||||
|
- recovery.go
|
||||||
|
|
||||||
|
- [ ] **7.7** 建立 types/ (5min)
|
||||||
|
- goctl 生成
|
||||||
|
|
||||||
|
- [ ] **7.8** 更新 import (30min)
|
||||||
|
- 批量更新
|
||||||
|
|
||||||
|
- [ ] **7.9** 驗證編譯 (10min)
|
||||||
|
|
||||||
|
- [ ] **7.10** 提交 (2min)
|
||||||
|
|
||||||
|
**預估時間**: ~4h
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 8: CLI Tools
|
||||||
|
|
||||||
|
### 分支
|
||||||
|
`refactor/cli`
|
||||||
|
|
||||||
|
### 依賴
|
||||||
|
Task 0 完成
|
||||||
|
|
||||||
|
### 小任務
|
||||||
|
|
||||||
|
- [ ] **8.1** 建立目錄 (1min)
|
||||||
|
|
||||||
|
- [ ] **8.2** 遷移 CLI 工具 (10min)
|
||||||
|
|
||||||
|
- [ ] **8.3** 遷移 gemini-login (5min)
|
||||||
|
|
||||||
|
- [ ] **8.4** 更新 import (5min)
|
||||||
|
|
||||||
|
- [ ] **8.5** 提交 (2min)
|
||||||
|
|
||||||
|
**預估時間**: ~30min
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Task 9: Cleanup & Tests
|
||||||
|
|
||||||
|
### 分支
|
||||||
|
`refactor/cleanup`
|
||||||
|
|
||||||
|
### 依賴
|
||||||
|
Task 7 完成
|
||||||
|
|
||||||
|
### 小任務
|
||||||
|
|
||||||
|
- [ ] **9.1** 移除舊目錄 (5min)
|
||||||
|
|
||||||
|
- [ ] **9.2** 更新 import (30min)
|
||||||
|
- 批量 sed
|
||||||
|
|
||||||
|
- [ ] **9.3** 建立 cmd/chat/chat.go (10min)
|
||||||
|
|
||||||
|
- [ ] **9.4** SSE 整合測試 (2h)
|
||||||
|
|
||||||
|
- [ ] **9.5** 回歸測試 (1h)
|
||||||
|
|
||||||
|
- [ ] **9.6** 更新 README (15min)
|
||||||
|
|
||||||
|
- [ ] **9.7** 提交 (2min)
|
||||||
|
|
||||||
|
**預估時間**: ~4h
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 並行執行計劃
|
||||||
|
|
||||||
|
### Wave 1 (可完全並行)
|
||||||
|
```
|
||||||
|
Terminal 1: Task 0 (init) → 30min
|
||||||
|
Terminal 2: (等待 Task 0)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Wave 2 (可完全並行)
|
||||||
|
```
|
||||||
|
Terminal 1: Task 1 (domain) → 2h
|
||||||
|
Terminal 2: Task 2 (infrastructure) → 1h
|
||||||
|
Terminal 3: Task 8 (cli) → 30min
|
||||||
|
```
|
||||||
|
|
||||||
|
### Wave 3 (可部分並行)
|
||||||
|
```
|
||||||
|
Terminal 1: Task 3 (repository) → 1h (依賴 Task 1)
|
||||||
|
Terminal 2: Task 4 (provider) → 30min (依賴 Task 1)
|
||||||
|
Terminal 3: Task 6 (adapter) → 30min (依賴 Task 1)
|
||||||
|
Terminal 4: (等待 Task 3)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Wave 4 (可部分並行)
|
||||||
|
```
|
||||||
|
Terminal 1: Task 5 (usecase) → 2h (依賴 Task 3)
|
||||||
|
Terminal 2: (等待 Task 5)
|
||||||
|
```
|
||||||
|
|
||||||
|
### Wave 5 (序列)
|
||||||
|
```
|
||||||
|
Task 7 (internal) → 4h
|
||||||
|
Task 9 (cleanup) → 4h
|
||||||
|
```
|
||||||
|
|
||||||
|
**總時間估計**:
|
||||||
|
- 完全序列: ~15h
|
||||||
|
- 並行執行: ~9h
|
||||||
|
- 節省: ~40%
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Git Worktree 指令
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 創建 worktrees
|
||||||
|
git worktree add ../worktrees/init -b refactor/init
|
||||||
|
git worktree add ../worktrees/domain -b refactor/domain
|
||||||
|
git worktree add ../worktrees/infrastructure -b refactor/infrastructure
|
||||||
|
git worktree add ../worktrees/repository -b refactor/repository
|
||||||
|
git worktree add ../worktrees/provider -b refactor/provider
|
||||||
|
git worktree add ../worktrees/usecase -b refactor/usecase
|
||||||
|
git worktree add ../worktrees/adapter -b refactor/adapter
|
||||||
|
git worktree add ../worktrees/cli -b refactor/cli
|
||||||
|
|
||||||
|
# 並行工作
|
||||||
|
cd ../worktrees/domain && # Terminal 1
|
||||||
|
cd ../worktrees/infrastructure && # Terminal 2
|
||||||
|
cd ../worktrees/cli && # Terminal 3
|
||||||
|
|
||||||
|
# 清理 worktrees
|
||||||
|
git worktree remove ../worktrees/init
|
||||||
|
git worktree remove ../worktrees/domain
|
||||||
|
# ... 等等
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**文件版本**: v1.0
|
||||||
|
**建立日期**: 2026-04-03
|
||||||
|
|
@ -1,18 +1,23 @@
|
||||||
// Code scaffolded by goctl. Safe to edit.
|
|
||||||
// goctl 1.10.1
|
|
||||||
|
|
||||||
package svc
|
package svc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"cursor-api-proxy/internal/config"
|
"cursor-api-proxy/internal/config"
|
||||||
|
domainrepo "cursor-api-proxy/pkg/domain/repository"
|
||||||
|
"cursor-api-proxy/pkg/repository"
|
||||||
)
|
)
|
||||||
|
|
||||||
type ServiceContext struct {
|
type ServiceContext struct {
|
||||||
Config config.Config
|
Config config.Config
|
||||||
|
|
||||||
|
// Domain services
|
||||||
|
AccountPool domainrepo.AccountPool
|
||||||
}
|
}
|
||||||
|
|
||||||
func NewServiceContext(c config.Config) *ServiceContext {
|
func NewServiceContext(c config.Config) *ServiceContext {
|
||||||
|
accountPool := repository.NewAccountPool(c.ConfigDirs)
|
||||||
|
|
||||||
return &ServiceContext{
|
return &ServiceContext{
|
||||||
Config: c,
|
Config: c,
|
||||||
|
AccountPool: accountPool,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ package geminiweb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"cursor-api-proxy/pkg/domain/entity"
|
|
||||||
"cursor-api-proxy/internal/config"
|
"cursor-api-proxy/internal/config"
|
||||||
|
"cursor-api-proxy/pkg/domain/entity"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -624,18 +624,39 @@ func (p *PlaywrightProvider) selectModel(model string) error {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// buildPromptFromMessages 從訊息列表建構提示詞
|
// buildPromptFromMessagesPlaywright 從訊息列表建構提示詞
|
||||||
func buildPromptFromMessagesPlaywright(messages []entity.Message) string {
|
func buildPromptFromMessagesPlaywright(messages []entity.Message) string {
|
||||||
var prompt string
|
var prompt string
|
||||||
for _, m := range messages {
|
for _, m := range messages {
|
||||||
|
content := messageContentToStringPlaywright(m.Content)
|
||||||
switch m.Role {
|
switch m.Role {
|
||||||
case "system":
|
case "system":
|
||||||
prompt += "System: " + m.Content + "\n\n"
|
prompt += "System: " + content + "\n\n"
|
||||||
case "user":
|
case "user":
|
||||||
prompt += m.Content + "\n\n"
|
prompt += content + "\n\n"
|
||||||
case "assistant":
|
case "assistant":
|
||||||
prompt += "Assistant: " + m.Content + "\n\n"
|
prompt += "Assistant: " + content + "\n\n"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return prompt
|
return prompt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// messageContentToStringPlaywright converts Message.Content to string
|
||||||
|
func messageContentToStringPlaywright(content interface{}) string {
|
||||||
|
switch v := content.(type) {
|
||||||
|
case string:
|
||||||
|
return v
|
||||||
|
case []interface{}:
|
||||||
|
var result string
|
||||||
|
for _, item := range v {
|
||||||
|
if m, ok := item.(map[string]interface{}); ok {
|
||||||
|
if text, ok := m["text"].(string); ok {
|
||||||
|
result += text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,8 @@ package geminiweb
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
"cursor-api-proxy/pkg/domain/entity"
|
|
||||||
"cursor-api-proxy/internal/config"
|
"cursor-api-proxy/internal/config"
|
||||||
|
"cursor-api-proxy/pkg/domain/entity"
|
||||||
"fmt"
|
"fmt"
|
||||||
"os"
|
"os"
|
||||||
"path/filepath"
|
"path/filepath"
|
||||||
|
|
@ -142,18 +142,40 @@ func (p *Provider) Generate(ctx context.Context, model string, messages []entity
|
||||||
func buildPromptFromMessages(messages []entity.Message) string {
|
func buildPromptFromMessages(messages []entity.Message) string {
|
||||||
var prompt string
|
var prompt string
|
||||||
for _, m := range messages {
|
for _, m := range messages {
|
||||||
|
content := messageContentToString(m.Content)
|
||||||
switch m.Role {
|
switch m.Role {
|
||||||
case "system":
|
case "system":
|
||||||
prompt += "System: " + m.Content + "\n\n"
|
prompt += "System: " + content + "\n\n"
|
||||||
case "user":
|
case "user":
|
||||||
prompt += m.Content + "\n\n"
|
prompt += content + "\n\n"
|
||||||
case "assistant":
|
case "assistant":
|
||||||
prompt += "Assistant: " + m.Content + "\n\n"
|
prompt += "Assistant: " + content + "\n\n"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return prompt
|
return prompt
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// messageContentToString converts Message.Content to string
|
||||||
|
func messageContentToString(content interface{}) string {
|
||||||
|
switch v := content.(type) {
|
||||||
|
case string:
|
||||||
|
return v
|
||||||
|
case []interface{}:
|
||||||
|
// Handle array content (multimodal)
|
||||||
|
var result string
|
||||||
|
for _, item := range v {
|
||||||
|
if m, ok := item.(map[string]interface{}); ok {
|
||||||
|
if text, ok := m["text"].(string); ok {
|
||||||
|
result += text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
default:
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// RunLogin 執行登入流程(供 gemini-login 命令使用)
|
// RunLogin 執行登入流程(供 gemini-login 命令使用)
|
||||||
func RunLogin(cfg config.BridgeConfig, sessionName string) error {
|
func RunLogin(cfg config.BridgeConfig, sessionName string) error {
|
||||||
if sessionName == "" {
|
if sessionName == "" {
|
||||||
|
|
|
||||||
|
|
@ -3,30 +3,20 @@ package repository
|
||||||
import (
|
import (
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
|
"cursor-api-proxy/pkg/domain/entity"
|
||||||
)
|
)
|
||||||
|
|
||||||
type accountStatus struct {
|
type accountStatus struct {
|
||||||
configDir string
|
configDir string
|
||||||
activeRequests int
|
activeRequests int
|
||||||
lastUsed int64
|
lastUsed int64
|
||||||
rateLimitUntil int64
|
rateLimitUntil int64
|
||||||
totalRequests int
|
totalRequests int
|
||||||
totalSuccess int
|
totalSuccess int
|
||||||
totalErrors int
|
totalErrors int
|
||||||
totalRateLimits int
|
totalRateLimits int
|
||||||
totalLatencyMs int64
|
totalLatencyMs int64
|
||||||
}
|
|
||||||
|
|
||||||
type AccountStat struct {
|
|
||||||
ConfigDir string
|
|
||||||
ActiveRequests int
|
|
||||||
TotalRequests int
|
|
||||||
TotalSuccess int
|
|
||||||
TotalErrors int
|
|
||||||
TotalRateLimits int
|
|
||||||
TotalLatencyMs int64
|
|
||||||
IsRateLimited bool
|
|
||||||
RateLimitUntil int64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type AccountPool struct {
|
type AccountPool struct {
|
||||||
|
|
@ -155,13 +145,13 @@ func (p *AccountPool) ReportRateLimit(configDir string, penaltyMs int64) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (p *AccountPool) GetStats() []AccountStat {
|
func (p *AccountPool) GetStats() []entity.AccountStat {
|
||||||
p.mu.Lock()
|
p.mu.Lock()
|
||||||
defer p.mu.Unlock()
|
defer p.mu.Unlock()
|
||||||
now := time.Now().UnixMilli()
|
now := time.Now().UnixMilli()
|
||||||
stats := make([]AccountStat, len(p.accounts))
|
stats := make([]entity.AccountStat, len(p.accounts))
|
||||||
for i, a := range p.accounts {
|
for i, a := range p.accounts {
|
||||||
stats[i] = AccountStat{
|
stats[i] = entity.AccountStat{
|
||||||
ConfigDir: a.configDir,
|
ConfigDir: a.configDir,
|
||||||
ActiveRequests: a.activeRequests,
|
ActiveRequests: a.activeRequests,
|
||||||
TotalRequests: a.totalRequests,
|
TotalRequests: a.totalRequests,
|
||||||
|
|
@ -180,7 +170,6 @@ func (p *AccountPool) Count() int {
|
||||||
return len(p.accounts)
|
return len(p.accounts)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// ─── PoolHandle interface ──────────────────────────────────────────────────
|
// ─── PoolHandle interface ──────────────────────────────────────────────────
|
||||||
// PoolHandle 讓 handler 可以注入獨立的 pool 實例,避免多 port 模式共用全域 pool。
|
// PoolHandle 讓 handler 可以注入獨立的 pool 實例,避免多 port 模式共用全域 pool。
|
||||||
|
|
||||||
|
|
@ -191,19 +180,19 @@ type PoolHandle interface {
|
||||||
ReportRequestSuccess(configDir string, latencyMs int64)
|
ReportRequestSuccess(configDir string, latencyMs int64)
|
||||||
ReportRequestError(configDir string, latencyMs int64)
|
ReportRequestError(configDir string, latencyMs int64)
|
||||||
ReportRateLimit(configDir string, penaltyMs int64)
|
ReportRateLimit(configDir string, penaltyMs int64)
|
||||||
GetStats() []AccountStat
|
GetStats() []entity.AccountStat
|
||||||
}
|
}
|
||||||
|
|
||||||
// GlobalPoolHandle 包裝全域函式以實作 PoolHandle 介面(單 port 模式使用)
|
// GlobalPoolHandle 包裝全域函式以實作 PoolHandle 介面(單 port 模式使用)
|
||||||
type GlobalPoolHandle struct{}
|
type GlobalPoolHandle struct{}
|
||||||
|
|
||||||
func (GlobalPoolHandle) GetNextConfigDir() string { return GetNextAccountConfigDir() }
|
func (GlobalPoolHandle) GetNextConfigDir() string { return GetNextAccountConfigDir() }
|
||||||
func (GlobalPoolHandle) ReportRequestStart(d string) { ReportRequestStart(d) }
|
func (GlobalPoolHandle) ReportRequestStart(d string) { ReportRequestStart(d) }
|
||||||
func (GlobalPoolHandle) ReportRequestEnd(d string) { ReportRequestEnd(d) }
|
func (GlobalPoolHandle) ReportRequestEnd(d string) { ReportRequestEnd(d) }
|
||||||
func (GlobalPoolHandle) ReportRequestSuccess(d string, l int64) { ReportRequestSuccess(d, l) }
|
func (GlobalPoolHandle) ReportRequestSuccess(d string, l int64) { ReportRequestSuccess(d, l) }
|
||||||
func (GlobalPoolHandle) ReportRequestError(d string, l int64) { ReportRequestError(d, l) }
|
func (GlobalPoolHandle) ReportRequestError(d string, l int64) { ReportRequestError(d, l) }
|
||||||
func (GlobalPoolHandle) ReportRateLimit(d string, p int64) { ReportRateLimit(d, p) }
|
func (GlobalPoolHandle) ReportRateLimit(d string, p int64) { ReportRateLimit(d, p) }
|
||||||
func (GlobalPoolHandle) GetStats() []AccountStat { return GetAccountStats() }
|
func (GlobalPoolHandle) GetStats() []entity.AccountStat { return GetAccountStats() }
|
||||||
|
|
||||||
// ─── Global pool ───────────────────────────────────────────────────────────
|
// ─── Global pool ───────────────────────────────────────────────────────────
|
||||||
|
|
||||||
|
|
@ -273,7 +262,7 @@ func ReportRateLimit(configDir string, penaltyMs int64) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func GetAccountStats() []AccountStat {
|
func GetAccountStats() []entity.AccountStat {
|
||||||
globalMu.Lock()
|
globalMu.Lock()
|
||||||
p := globalPool
|
p := globalPool
|
||||||
globalMu.Unlock()
|
globalMu.Unlock()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue