Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions docs/models_api.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# /v1/models API 使用说明

## 功能概述
ccNexus 现已支持 OpenAI 兼容的 `/v1/models` API,聚合所有后端端点的模型列表。

## 快速开始

### 启动服务
```bash
go run ./cmd/server
```

### 获取模型列表
```bash
curl http://localhost:3000/v1/models
```

### 强制刷新缓存
```bash
curl http://localhost:3000/v1/models?refresh=true
```

## 响应示例
```json
{
"object": "list",
"data": [
{
"id": "claude-sonnet-4-20250514",
"object": "model",
"created": 1700000000,
"owned_by": "anthropic",
"endpoint_id": "Claude Official"
}
]
}
```

## 配置项
在 `config.json` 中添加:
```json
{
"modelsCacheTTL": 30 // 缓存时间(分钟),默认30
}
```

## 支持的端点
- **openai/openai2**: 自动查询后端 /v1/models
- **gemini**: 自动查询后端 /v1beta/models
- **claude**: 使用配置的 model 字段(无API查询)

## 特性
- ✅ 聚合多后端模型列表
- ✅ 自动缓存(30分钟,可配置)
- ✅ 支持刷新参数
- ✅ 失败降级(返回默认模型)
23 changes: 23 additions & 0 deletions internal/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,8 @@ type Config struct {
CloseWindowBehavior string `json:"closeWindowBehavior,omitempty"` // "quit", "minimize", "ask"
ClaudeNotificationEnabled bool `json:"claudeNotificationEnabled"` // Enable Claude Code task completion notification
ClaudeNotificationType string `json:"claudeNotificationType"` // Notification type: toast, dialog, disabled
ModelsCacheTTL int `json:"modelsCacheTTL,omitempty"` // /v1/models cache TTL in minutes, default 30
ModelsCacheRefreshEnabled bool `json:"modelsCacheRefreshEnabled,omitempty"` // Enable ?refresh=true parameter, default false
WebDAV *WebDAVConfig `json:"webdav,omitempty"` // WebDAV synchronization config
Backup *BackupConfig `json:"backup,omitempty"` // Backup/sync configuration
Update *UpdateConfig `json:"update,omitempty"` // Update configuration
Expand All @@ -189,6 +191,8 @@ func DefaultConfig() *Config {
Language: "zh-CN", // Default to Chinese
WindowWidth: 1024, // Default window width
WindowHeight: 768, // Default window height
ModelsCacheTTL: 30, // Default 30 minutes
ModelsCacheRefreshEnabled: false, // Default disabled
Endpoints: []Endpoint{
{
Name: "Claude Official",
Expand Down Expand Up @@ -575,6 +579,19 @@ func LoadFromStorage(storage StorageAdapter) (*Config, error) {
}
}

if modelsCacheTTLStr, err := storage.GetConfig("modelsCacheTTL"); err == nil && modelsCacheTTLStr != "" {
if modelsCacheTTL, err := strconv.Atoi(modelsCacheTTLStr); err == nil {
config.ModelsCacheTTL = modelsCacheTTL
}
}
if config.ModelsCacheTTL == 0 {
config.ModelsCacheTTL = 30 // Default 30 minutes
}

if modelsCacheRefreshEnabledStr, err := storage.GetConfig("modelsCacheRefreshEnabled"); err == nil && modelsCacheRefreshEnabledStr != "" {
config.ModelsCacheRefreshEnabled = modelsCacheRefreshEnabledStr == "true"
}

if lang, err := storage.GetConfig("language"); err == nil {
config.Language = lang
}
Expand Down Expand Up @@ -820,6 +837,12 @@ func (c *Config) SaveToStorage(storage StorageAdapter) error {
if err := storage.SetConfig("logLevel", strconv.Itoa(c.LogLevel)); err != nil {
return fmt.Errorf("failed to save logLevel config: %w", err)
}
if err := storage.SetConfig("modelsCacheTTL", strconv.Itoa(c.ModelsCacheTTL)); err != nil {
return fmt.Errorf("failed to save modelsCacheTTL config: %w", err)
}
if err := storage.SetConfig("modelsCacheRefreshEnabled", strconv.FormatBool(c.ModelsCacheRefreshEnabled)); err != nil {
return fmt.Errorf("failed to save modelsCacheRefreshEnabled config: %w", err)
}
if err := storage.SetConfig("language", c.Language); err != nil {
return fmt.Errorf("failed to save language config: %w", err)
}
Expand Down
6 changes: 6 additions & 0 deletions internal/proxy/handler.go
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,12 @@ func (p *Proxy) UpdateConfig(cfg *config.Config) error {
p.currentIndex = 0
}

// Clear models cache to force refresh with new endpoints
if p.modelsCache != nil {
p.modelsCache.Clear()
logger.Debug("[CONFIG UPDATE] Cleared models cache")
}

logger.Info("Configuration updated: %d endpoints configured", len(cfg.GetEndpoints()))
return nil
}
Loading
Loading