Merge branch 'main' of https://github.com/sipeed/picoclaw into feat-azure-openai-support

This commit is contained in:
Kunal Karmakar 2026-03-13 04:27:18 +00:00
commit ea09139c9f
9 changed files with 163 additions and 57 deletions

View file

@ -206,7 +206,7 @@ docker compose -f docker/docker-compose.yml --profile gateway up -d
### 🚀 Démarrage Rapide
> [!TIP]
> Configurez votre clé API dans `~/.picoclaw/config.json`. Obtenez des clés API : [Volcengine (CodingPlan)](https://console.volcengine.com) (LLM) · [OpenRouter](https://openrouter.ai/keys) (LLM) · [Zhipu](https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys) (LLM). La recherche web est optionnelle — obtenez gratuitement l'[API Tavily](https://tavily.com) (1000 requêtes gratuites/mois) ou l'[API Brave Search](https://brave.com/search/api) (2000 requêtes gratuites/mois).
> Configurez votre clé API dans `~/.picoclaw/config.json`. Obtenez des clés API : [Volcengine (CodingPlan)](https://www.volcengine.com/activity/codingplan?utm_campaign=PicoClaw&utm_content=PicoClaw&utm_medium=devrel&utm_source=OWO&utm_term=PicoClaw) (LLM) · [OpenRouter](https://openrouter.ai/keys) (LLM) · [Zhipu](https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys) (LLM). La recherche web est optionnelle — obtenez gratuitement l'[API Tavily](https://tavily.com) (1000 requêtes gratuites/mois) ou l'[API Brave Search](https://brave.com/search/api) (2000 requêtes gratuites/mois).
**1. Initialiser**
@ -835,6 +835,7 @@ Le sous-agent a accès aux outils (message, web_search, etc.) et peut communique
| ------------------------ | ---------------------------------------- | ------------------------------------------------------ |
| `gemini` | LLM (Gemini direct) | [aistudio.google.com](https://aistudio.google.com) |
| `zhipu` | LLM (Zhipu direct) | [bigmodel.cn](bigmodel.cn) |
| `volcengine` | LLM(Volcengine direct) | [volcengine.com](https://www.volcengine.com/activity/codingplan?utm_campaign=PicoClaw&utm_content=PicoClaw&utm_medium=devrel&utm_source=OWO&utm_term=PicoClaw) |
| `openrouter` (À tester) | LLM (recommandé, accès à tous les modèles) | [openrouter.ai](https://openrouter.ai) |
| `anthropic` (À tester) | LLM (Claude direct) | [console.anthropic.com](https://console.anthropic.com) |
| `openai` (À tester) | LLM (GPT direct) | [platform.openai.com](https://platform.openai.com) |
@ -980,9 +981,9 @@ Cette conception permet également le **support multi-agent** avec une sélectio
| **OpenRouter** | `openrouter/` | `https://openrouter.ai/api/v1` | OpenAI | [Obtenir Clé](https://openrouter.ai/keys) |
| **VLLM** | `vllm/` | `http://localhost:8000/v1` | OpenAI | Local |
| **Cerebras** | `cerebras/` | `https://api.cerebras.ai/v1` | OpenAI | [Obtenir Clé](https://cerebras.ai) |
| **VolcEngine (Doubao)** | `volcengine/` | `https://ark.cn-beijing.volces.com/api/v3` | OpenAI | [Obtenir Clé](https://console.volcengine.com) |
| **VolcEngine (Doubao)** | `volcengine/` | `https://ark.cn-beijing.volces.com/api/v3` | OpenAI | [Obtenir Clé](https://www.volcengine.com/activity/codingplan?utm_campaign=PicoClaw&utm_content=PicoClaw&utm_medium=devrel&utm_source=OWO&utm_term=PicoClaw) |
| **ShengsuanYun** | `shengsuanyun/` | `https://router.shengsuanyun.com/api/v1` | OpenAI | - |
| **BytePlus** | `byteplus/` | `https://ark.ap-southeast.bytepluses.com/api/v3` | OpenAI | [Obtenir Clé](https://console.volcengine.com) |
| **BytePlus** | `byteplus/` | `https://ark.ap-southeast.bytepluses.com/api/v3` | OpenAI | [Obtenir Clé](https://www.byteplus.com/) |
| **LongCat** | `longcat/` | `https://api.longcat.chat/openai` | OpenAI | [Obtenir une clé](https://longcat.chat/platform) |
| **Azure OpenAI** | `azure/` | `https://{resource}.openai.azure.com` | Azure | [Obtenir Clé](https://portal.azure.com) |
| **Antigravity** | `antigravity/` | Google Cloud | Custom | OAuth uniquement |

View file

@ -168,7 +168,7 @@ docker compose -f docker/docker-compose.yml --profile gateway up -d
### 🚀 クイックスタート(ネイティブ)
> [!TIP]
> `~/.picoclaw/config.json` に API キーを設定してください。API キーの取得先: [Volcengine (CodingPlan)](https://console.volcengine.com) (LLM) · [OpenRouter](https://openrouter.ai/keys) (LLM) · [Zhipu](https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys) (LLM)。Web 検索は **任意** です — 無料の [Tavily API](https://tavily.com) (月 1000 クエリ無料) または [Brave Search API](https://brave.com/search/api) (月 2000 クエリ無料)。
> `~/.picoclaw/config.json` に API キーを設定してください。API キーの取得先: [Volcengine (CodingPlan)](https://www.volcengine.com/activity/codingplan?utm_campaign=PicoClaw&utm_content=PicoClaw&utm_medium=devrel&utm_source=OWO&utm_term=PicoClaw) (LLM) · [OpenRouter](https://openrouter.ai/keys) (LLM) · [Zhipu](https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys) (LLM)。Web 検索は **任意** です — 無料の [Tavily API](https://tavily.com) (月 1000 クエリ無料) または [Brave Search API](https://brave.com/search/api) (月 2000 クエリ無料)。
**1. 初期化**
@ -793,6 +793,7 @@ HEARTBEAT_OK 応答 ユーザーが直接結果を受け取る
| --- | --- | --- |
| `gemini` | LLMGemini 直接) | [aistudio.google.com](https://aistudio.google.com) |
| `zhipu` | LLMZhipu 直接) | [bigmodel.cn](https://bigmodel.cn) |
| `volcengine` | LLM(Volcengine 直接) | [volcengine.com](https://www.volcengine.com/activity/codingplan?utm_campaign=PicoClaw&utm_content=PicoClaw&utm_medium=devrel&utm_source=OWO&utm_term=PicoClaw) |
| `openrouter`(要テスト) | LLM推奨、全モデルにアクセス可能 | [openrouter.ai](https://openrouter.ai) |
| `anthropic`(要テスト) | LLMClaude 直接) | [console.anthropic.com](https://console.anthropic.com) |
| `openai`(要テスト) | LLMGPT 直接) | [platform.openai.com](https://platform.openai.com) |
@ -921,9 +922,9 @@ HEARTBEAT_OK 応答 ユーザーが直接結果を受け取る
| **OpenRouter** | `openrouter/` | `https://openrouter.ai/api/v1` | OpenAI | [キーを取得](https://openrouter.ai/keys) |
| **VLLM** | `vllm/` | `http://localhost:8000/v1` | OpenAI | ローカル |
| **Cerebras** | `cerebras/` | `https://api.cerebras.ai/v1` | OpenAI | [キーを取得](https://cerebras.ai) |
| **VolcEngine (Doubao)** | `volcengine/` | `https://ark.cn-beijing.volces.com/api/v3` | OpenAI | [キーを取得](https://console.volcengine.com) |
| **VolcEngine (Doubao)** | `volcengine/` | `https://ark.cn-beijing.volces.com/api/v3` | OpenAI | [キーを取得](https://www.volcengine.com/activity/codingplan?utm_campaign=PicoClaw&utm_content=PicoClaw&utm_medium=devrel&utm_source=OWO&utm_term=PicoClaw) |
| **ShengsuanYun** | `shengsuanyun/` | `https://router.shengsuanyun.com/api/v1` | OpenAI | - |
| **BytePlus** | `byteplus/` | `https://ark.ap-southeast.bytepluses.com/api/v3` | OpenAI | [キーを取得](https://console.volcengine.com) |
| **BytePlus** | `byteplus/` | `https://ark.ap-southeast.bytepluses.com/api/v3` | OpenAI | [キーを取得](https://www.byteplus.com) |
| **LongCat** | `longcat/` | `https://api.longcat.chat/openai` | OpenAI | [キーを取得](https://longcat.chat/platform) |
| **Azure OpenAI** | `azure/` | `https://{resource}.openai.azure.com` | Azure | [キーを取得](https://portal.azure.com) |
| **Antigravity** | `antigravity/` | Google Cloud | カスタム | OAuthのみ |

View file

@ -227,7 +227,7 @@ docker compose -f docker/docker-compose.yml --profile gateway up -d
### 🚀 Quick Start
> [!TIP]
> Set your API Key in `~/.picoclaw/config.json`. Get API Keys: [Volcengine (CodingPlan)](https://console.volcengine.com) (LLM) · [OpenRouter](https://openrouter.ai/keys) (LLM) · [Zhipu](https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys) (LLM). Web search is optional — get a free [Tavily API](https://tavily.com) (1000 free queries/month) or [Brave Search API](https://brave.com/search/api) (2000 free queries/month).
> Set your API Key in `~/.picoclaw/config.json`. Get API Keys: [Volcengine (CodingPlan)](https://www.volcengine.com/activity/codingplan?utm_campaign=PicoClaw&utm_content=PicoClaw&utm_medium=devrel&utm_source=OWO&utm_term=PicoClaw) (LLM) · [OpenRouter](https://openrouter.ai/keys) (LLM) · [Zhipu](https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys) (LLM). Web search is optional — get a free [Tavily API](https://tavily.com) (1000 free queries/month) or [Brave Search API](https://brave.com/search/api) (2000 free queries/month).
**1. Initialize**
@ -992,18 +992,18 @@ The subagent has access to tools (message, web_search, etc.) and can communicate
> Groq provides free voice transcription via Whisper. If configured, audio messages from any channel will be automatically transcribed at the agent level.
| Provider | Purpose | Get API Key |
| -------------------------- | --------------------------------------- | -------------------------------------------------------------------- |
| ------------ | --------------------------------------- | ------------------------------------------------------------ |
| `gemini` | LLM (Gemini direct) | [aistudio.google.com](https://aistudio.google.com) |
| `zhipu` | LLM (Zhipu direct) | [bigmodel.cn](https://bigmodel.cn) |
| `openrouter(To be tested)` | LLM (recommended, access to all models) | [openrouter.ai](https://openrouter.ai) |
| `anthropic(To be tested)` | LLM (Claude direct) | [console.anthropic.com](https://console.anthropic.com) |
| `openai(To be tested)` | LLM (GPT direct) | [platform.openai.com](https://platform.openai.com) |
| `deepseek(To be tested)` | LLM (DeepSeek direct) | [platform.deepseek.com](https://platform.deepseek.com) |
| `volcengine` | LLM(Volcengine direct) | [volcengine.com](https://www.volcengine.com/activity/codingplan?utm_campaign=PicoClaw&utm_content=PicoClaw&utm_medium=devrel&utm_source=OWO&utm_term=PicoClaw) |
| `openrouter` | LLM (recommended, access to all models) | [openrouter.ai](https://openrouter.ai) |
| `anthropic` | LLM (Claude direct) | [console.anthropic.com](https://console.anthropic.com) |
| `openai` | LLM (GPT direct) | [platform.openai.com](https://platform.openai.com) |
| `deepseek` | LLM (DeepSeek direct) | [platform.deepseek.com](https://platform.deepseek.com) |
| `qwen` | LLM (Qwen direct) | [dashscope.console.aliyun.com](https://dashscope.console.aliyun.com) |
| `groq` | LLM + **Voice transcription** (Whisper) | [console.groq.com](https://console.groq.com) |
| `cerebras` | LLM (Cerebras direct) | [cerebras.ai](https://cerebras.ai) |
| `vivgrid` | LLM (Vivgrid direct) | [vivgrid.com](https://vivgrid.com) |
| `azure` | LLM (Azure OpenAI) | [portal.azure.com](https://portal.azure.com) |
### Model Configuration (model_list)
@ -1034,9 +1034,9 @@ This design also enables **multi-agent support** with flexible provider selectio
| **LiteLLM Proxy** | `litellm/` | `http://localhost:4000/v1` | OpenAI | Your LiteLLM proxy key |
| **VLLM** | `vllm/` | `http://localhost:8000/v1` | OpenAI | Local |
| **Cerebras** | `cerebras/` | `https://api.cerebras.ai/v1` | OpenAI | [Get Key](https://cerebras.ai) |
| **VolcEngine (Doubao)** | `volcengine/` | `https://ark.cn-beijing.volces.com/api/v3` | OpenAI | [Get Key](https://console.volcengine.com) |
| **VolcEngine (Doubao)** | `volcengine/` | `https://ark.cn-beijing.volces.com/api/v3` | OpenAI | [Get Key](https://www.volcengine.com/activity/codingplan?utm_campaign=PicoClaw&utm_content=PicoClaw&utm_medium=devrel&utm_source=OWO&utm_term=PicoClaw) |
| **神算云** | `shengsuanyun/` | `https://router.shengsuanyun.com/api/v1` | OpenAI | - |
| **BytePlus** | `byteplus/` | `https://ark.ap-southeast.bytepluses.com/api/v3` | OpenAI | [Get Key](https://console.volcengine.com) |
| **BytePlus** | `byteplus/` | `https://ark.ap-southeast.bytepluses.com/api/v3` | OpenAI | [Get Key](https://www.byteplus.com) |
| **Vivgrid** | `vivgrid/` | `https://api.vivgrid.com/v1` | OpenAI | [Get Key](https://vivgrid.com) |
| **LongCat** | `longcat/` | `https://api.longcat.chat/openai` | OpenAI | [Get Key](https://longcat.chat/platform) |
| **Azure OpenAI** | `azure/` | `https://{resource}.openai.azure.com` | Azure | [Get Key](https://portal.azure.com) |

View file

@ -207,7 +207,7 @@ docker compose -f docker/docker-compose.yml --profile gateway up -d
### 🚀 Início Rápido
> [!TIP]
> Configure sua API key em `~/.picoclaw/config.json`. Obtenha API keys: [Volcengine (CodingPlan)](https://console.volcengine.com) (LLM) · [OpenRouter](https://openrouter.ai/keys) (LLM) · [Zhipu](https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys) (LLM). Busca web é **opcional** — obtenha a [API Tavily](https://tavily.com) gratuita (1000 consultas grátis/mês) ou a [Brave Search API](https://brave.com/search/api) (2000 consultas grátis/mês).
> Configure sua API key em `~/.picoclaw/config.json`. Obtenha API keys: [Volcengine (CodingPlan)](https://www.volcengine.com/activity/codingplan?utm_campaign=PicoClaw&utm_content=PicoClaw&utm_medium=devrel&utm_source=OWO&utm_term=PicoClaw) (LLM) · [OpenRouter](https://openrouter.ai/keys) (LLM) · [Zhipu](https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys) (LLM). Busca web é **opcional** — obtenha a [API Tavily](https://tavily.com) gratuita (1000 consultas grátis/mês) ou a [Brave Search API](https://brave.com/search/api) (2000 consultas grátis/mês).
**1. Inicializar**
@ -831,6 +831,7 @@ O subagente tem acesso às ferramentas (message, web_search, etc.) e pode se com
| --- | --- | --- |
| `gemini` | LLM (Gemini direto) | [aistudio.google.com](https://aistudio.google.com) |
| `zhipu` | LLM (Zhipu direto) | [bigmodel.cn](bigmodel.cn) |
| `volcengine` | LLM(Volcengine direto) | [volcengine.com](https://www.volcengine.com/activity/codingplan?utm_campaign=PicoClaw&utm_content=PicoClaw&utm_medium=devrel&utm_source=OWO&utm_term=PicoClaw) |
| `openrouter` (Em teste) | LLM (recomendado, acesso a todos os modelos) | [openrouter.ai](https://openrouter.ai) |
| `anthropic` (Em teste) | LLM (Claude direto) | [console.anthropic.com](https://console.anthropic.com) |
| `openai` (Em teste) | LLM (GPT direto) | [platform.openai.com](https://platform.openai.com) |
@ -976,9 +977,9 @@ Este design também possibilita o **suporte multi-agent** com seleção flexíve
| **OpenRouter** | `openrouter/` | `https://openrouter.ai/api/v1` | OpenAI | [Obter Chave](https://openrouter.ai/keys) |
| **VLLM** | `vllm/` | `http://localhost:8000/v1` | OpenAI | Local |
| **Cerebras** | `cerebras/` | `https://api.cerebras.ai/v1` | OpenAI | [Obter Chave](https://cerebras.ai) |
| **VolcEngine (Doubao)** | `volcengine/` | `https://ark.cn-beijing.volces.com/api/v3` | OpenAI | [Obter Chave](https://console.volcengine.com) |
| **VolcEngine (Doubao)** | `volcengine/` | `https://ark.cn-beijing.volces.com/api/v3` | OpenAI | [Obter Chave](https://www.volcengine.com/activity/codingplan?utm_campaign=PicoClaw&utm_content=PicoClaw&utm_medium=devrel&utm_source=OWO&utm_term=PicoClaw) |
| **ShengsuanYun** | `shengsuanyun/` | `https://router.shengsuanyun.com/api/v1` | OpenAI | - |
| **BytePlus** | `byteplus/` | `https://ark.ap-southeast.bytepluses.com/api/v3` | OpenAI | [Obter Chave](https://console.volcengine.com) |
| **BytePlus** | `byteplus/` | `https://ark.ap-southeast.bytepluses.com/api/v3` | OpenAI | [Obter Chave](https://www.byteplus.com) |
| **LongCat** | `longcat/` | `https://api.longcat.chat/openai` | OpenAI | [Obter Chave](https://longcat.chat/platform) |
| **Azure OpenAI** | `azure/` | `https://{resource}.openai.azure.com` | Azure | [Obter Chave](https://portal.azure.com) |
| **Antigravity** | `antigravity/` | Google Cloud | Custom | Apenas OAuth |

View file

@ -187,7 +187,7 @@ docker compose -f docker/docker-compose.yml --profile gateway up -d
### 🚀 Bắt đầu nhanh
> [!TIP]
> Thiết lập API key trong `~/.picoclaw/config.json`. Lấy API key: [Volcengine (CodingPlan)](https://console.volcengine.com) (LLM) · [OpenRouter](https://openrouter.ai/keys) (LLM) · [Zhipu](https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys) (LLM). Tìm kiếm web là **tùy chọn** — lấy [Tavily API](https://tavily.com) miễn phí (1000 truy vấn/tháng) hoặc [Brave Search API](https://brave.com/search/api) (2000 truy vấn/tháng).
> Thiết lập API key trong `~/.picoclaw/config.json`. Lấy API key: [Volcengine (CodingPlan)](https://www.volcengine.com/activity/codingplan?utm_campaign=PicoClaw&utm_content=PicoClaw&utm_medium=devrel&utm_source=OWO&utm_term=PicoClaw) (LLM) · [OpenRouter](https://openrouter.ai/keys) (LLM) · [Zhipu](https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys) (LLM). Tìm kiếm web là **tùy chọn** — lấy [Tavily API](https://tavily.com) miễn phí (1000 truy vấn/tháng) hoặc [Brave Search API](https://brave.com/search/api) (2000 truy vấn/tháng).
**1. Khởi tạo**
@ -803,6 +803,7 @@ Subagent có quyền truy cập các công cụ (message, web_search, v.v.) và
| --- | --- | --- |
| `gemini` | LLM (Gemini trực tiếp) | [aistudio.google.com](https://aistudio.google.com) |
| `zhipu` | LLM (Zhipu trực tiếp) | [bigmodel.cn](bigmodel.cn) |
| `volcengine` | LLM(Volcengine trực tiếp) | [volcengine.com](https://www.volcengine.com/activity/codingplan?utm_campaign=PicoClaw&utm_content=PicoClaw&utm_medium=devrel&utm_source=OWO&utm_term=PicoClaw) |
| `openrouter` (Đang thử nghiệm) | LLM (khuyên dùng, truy cập mọi model) | [openrouter.ai](https://openrouter.ai) |
| `anthropic` (Đang thử nghiệm) | LLM (Claude trực tiếp) | [console.anthropic.com](https://console.anthropic.com) |
| `openai` (Đang thử nghiệm) | LLM (GPT trực tiếp) | [platform.openai.com](https://platform.openai.com) |
@ -945,9 +946,9 @@ Thiết kế này cũng cho phép **hỗ trợ đa tác nhân** với lựa ch
| **OpenRouter** | `openrouter/` | `https://openrouter.ai/api/v1` | OpenAI | [Lấy Khóa](https://openrouter.ai/keys) |
| **VLLM** | `vllm/` | `http://localhost:8000/v1` | OpenAI | Local |
| **Cerebras** | `cerebras/` | `https://api.cerebras.ai/v1` | OpenAI | [Lấy Khóa](https://cerebras.ai) |
| **VolcEngine (Doubao)** | `volcengine/` | `https://ark.cn-beijing.volces.com/api/v3` | OpenAI | [Lấy Khóa](https://console.volcengine.com) |
| **VolcEngine (Doubao)** | `volcengine/` | `https://ark.cn-beijing.volces.com/api/v3` | OpenAI | [Lấy Khóa](https://www.volcengine.com/activity/codingplan?utm_campaign=PicoClaw&utm_content=PicoClaw&utm_medium=devrel&utm_source=OWO&utm_term=PicoClaw) |
| **ShengsuanYun** | `shengsuanyun/` | `https://router.shengsuanyun.com/api/v1` | OpenAI | - |
| **BytePlus** | `byteplus/` | `https://ark.ap-southeast.bytepluses.com/api/v3` | OpenAI | [Lấy Khóa](https://console.volcengine.com) |
| **BytePlus** | `byteplus/` | `https://ark.ap-southeast.bytepluses.com/api/v3` | OpenAI | [Lấy Khóa](https://www.byteplus.com) |
| **LongCat** | `longcat/` | `https://api.longcat.chat/openai` | OpenAI | [Lấy Key](https://longcat.chat/platform) |
| **Azure OpenAI** | `azure/` | `https://{resource}.openai.azure.com` | Azure | [Lấy Khóa](https://portal.azure.com) |
| **Antigravity** | `antigravity/` | Google Cloud | Tùy chỉnh | Chỉ OAuth |

View file

@ -208,7 +208,7 @@ docker compose -f docker/docker-compose.yml --profile gateway up -d
### 🚀 快速开始
> [!TIP]
> 在 `~/.picoclaw/config.json` 中设置您的 API Key。获取 API Key: [火山引擎 (CodingPlan)](https://console.volcengine.com) (LLM) · [OpenRouter](https://openrouter.ai/keys) (LLM) · [Zhipu (智谱)](https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys) (LLM)。网络搜索是 **可选的** — 获取免费的 [Tavily API](https://tavily.com) (每月 1000 次免费查询) 或 [Brave Search API](https://brave.com/search/api) (每月 2000 次免费查询)。
> 在 `~/.picoclaw/config.json` 中设置您的 API Key。获取 API Key: [火山引擎 (CodingPlan)](https://www.volcengine.com/activity/codingplan?utm_campaign=PicoClaw&utm_content=PicoClaw&utm_medium=devrel&utm_source=OWO&utm_term=PicoClaw) (LLM) · [OpenRouter](https://openrouter.ai/keys) (LLM) · [Zhipu (智谱)](https://open.bigmodel.cn/usercenter/proj-mgmt/apikeys) (LLM)。网络搜索是 **可选的** — 获取免费的 [Tavily API](https://tavily.com) (每月 1000 次免费查询) 或 [Brave Search API](https://brave.com/search/api) (每月 2000 次免费查询)。
**1. 初始化 (Initialize)**
@ -481,10 +481,11 @@ Agent 读取 HEARTBEAT.md
| -------------------- | ---------------------------- | -------------------------------------------------------------------- |
| `gemini` | LLM (Gemini 直连) | [aistudio.google.com](https://aistudio.google.com) |
| `zhipu` | LLM (智谱直连) | [bigmodel.cn](bigmodel.cn) |
| `openrouter(待测试)` | LLM (推荐,可访问所有模型) | [openrouter.ai](https://openrouter.ai) |
| `anthropic(待测试)` | LLM (Claude 直连) | [console.anthropic.com](https://console.anthropic.com) |
| `openai(待测试)` | LLM (GPT 直连) | [platform.openai.com](https://platform.openai.com) |
| `deepseek(待测试)` | LLM (DeepSeek 直连) | [platform.deepseek.com](https://platform.deepseek.com) |
| `volcengine` | LLM (火山引擎直连) | [volcengine.com](https://www.volcengine.com/activity/codingplan?utm_campaign=PicoClaw&utm_content=PicoClaw&utm_medium=devrel&utm_source=OWO&utm_term=PicoClaw) |
| `openrouter` | LLM (推荐,可访问所有模型) | [openrouter.ai](https://openrouter.ai) |
| `anthropic` | LLM (Claude 直连) | [console.anthropic.com](https://console.anthropic.com) |
| `openai` | LLM (GPT 直连) | [platform.openai.com](https://platform.openai.com) |
| `deepseek` | LLM (DeepSeek 直连) | [platform.deepseek.com](https://platform.deepseek.com) |
| `qwen` | LLM (通义千问) | [dashscope.console.aliyun.com](https://dashscope.console.aliyun.com) |
| `groq` | LLM + **语音转录** (Whisper) | [console.groq.com](https://console.groq.com) |
| `cerebras` | LLM (Cerebras 直连) | [cerebras.ai](https://cerebras.ai) |
@ -517,9 +518,9 @@ Agent 读取 HEARTBEAT.md
| **OpenRouter** | `openrouter/` | `https://openrouter.ai/api/v1` | OpenAI | [获取密钥](https://openrouter.ai/keys) |
| **VLLM** | `vllm/` | `http://localhost:8000/v1` | OpenAI | 本地 |
| **Cerebras** | `cerebras/` | `https://api.cerebras.ai/v1` | OpenAI | [获取密钥](https://cerebras.ai) |
| **火山引擎Doubao** | `volcengine/` | `https://ark.cn-beijing.volces.com/api/v3` | OpenAI | [获取密钥](https://console.volcengine.com) |
| **火山引擎Doubao** | `volcengine/` | `https://ark.cn-beijing.volces.com/api/v3` | OpenAI | [获取密钥](https://www.volcengine.com/activity/codingplan?utm_campaign=PicoClaw&utm_content=PicoClaw&utm_medium=devrel&utm_source=OWO&utm_term=PicoClaw) |
| **神算云** | `shengsuanyun/` | `https://router.shengsuanyun.com/api/v1` | OpenAI | - |
| **BytePlus** | `byteplus/` | `https://ark.ap-southeast.bytepluses.com/api/v3` | OpenAI | [获取密钥](https://console.volcengine.com) |
| **BytePlus** | `byteplus/` | `https://ark.ap-southeast.bytepluses.com/api/v3` | OpenAI | [获取密钥](https://www.byteplus.com) |
| **LongCat** | `longcat/` | `https://api.longcat.chat/openai` | OpenAI | [获取密钥](https://longcat.chat/platform) |
| **Azure OpenAI** | `azure/` | `https://{resource}.openai.azure.com` | Azure | [获取密钥](https://portal.azure.com) |
| **Antigravity** | `antigravity/` | Google Cloud | 自定义 | 仅 OAuth |

View file

@ -113,6 +113,7 @@ func getCallerInfo() (string, int, string) {
// bypass common loggers
if strings.HasSuffix(file, "/logger.go") ||
strings.HasSuffix(file, "/logger_3rd_party.go") ||
strings.HasSuffix(file, "/log.go") {
continue
}

View file

@ -4,6 +4,7 @@ import (
"embed"
"io/fs"
"log"
"mime"
"net/http"
"path"
"strings"
@ -14,6 +15,13 @@ var frontendFS embed.FS
// registerEmbedRoutes sets up the HTTP handler to serve the embedded frontend files
func registerEmbedRoutes(mux *http.ServeMux) {
// Register correct MIME type for SVG files
// Go's built-in mime.TypeByExtension returns "image/svg" which is incorrect
// The correct MIME type per RFC 6838 is "image/svg+xml"
if err := mime.AddExtensionType(".svg", "image/svg+xml"); err != nil {
log.Printf("Warning: failed to register SVG MIME type: %v", err)
}
// Attempt to get the subdirectory 'dist' where Vite usually builds
subFS, err := fs.Sub(frontendFS, "dist")
if err != nil {

View file

@ -1,6 +1,12 @@
import dayjs from "dayjs"
import { useAtomValue } from "jotai"
import { useCallback, useEffect, useRef, useState } from "react"
import {
type SetStateAction,
useCallback,
useEffect,
useRef,
useState,
} from "react"
import { useTranslation } from "react-i18next"
import { toast } from "sonner"
@ -26,6 +32,22 @@ export interface ChatMessage {
type ConnectionState = "disconnected" | "connecting" | "connected" | "error"
const LAST_SESSION_STORAGE_KEY = "picoclaw:last-session-id"
function readStoredSessionId(): string {
const value = localStorage.getItem(LAST_SESSION_STORAGE_KEY)?.trim()
return value || ""
}
function writeStoredSessionId(sessionId: string) {
if (sessionId) {
localStorage.setItem(LAST_SESSION_STORAGE_KEY, sessionId)
return
}
localStorage.removeItem(LAST_SESSION_STORAGE_KEY)
}
function generateSessionId(): string {
const webCrypto = globalThis.crypto
if (webCrypto && typeof webCrypto.randomUUID === "function") {
@ -109,18 +131,95 @@ export function usePicoChat() {
useState<ConnectionState>("disconnected")
const [isTyping, setIsTyping] = useState(false)
const [activeSessionId, setActiveSessionId] =
useState<string>(generateSessionId)
useState<string>(() => readStoredSessionId() || generateSessionId())
const wsRef = useRef<WebSocket | null>(null)
const isConnectingRef = useRef(false)
const msgIdCounter = useRef(0)
const activeSessionIdRef = useRef(activeSessionId)
const messagesRevisionRef = useRef(0)
const setTrackedMessages = useCallback(
(nextState: SetStateAction<ChatMessage[]>) => {
setMessages((prev) => {
const next =
typeof nextState === "function"
? (
nextState as (prevState: ChatMessage[]) => ChatMessage[]
)(prev)
: nextState
if (next !== prev) {
messagesRevisionRef.current += 1
}
return next
})
},
[],
)
// Keep ref in sync
useEffect(() => {
activeSessionIdRef.current = activeSessionId
writeStoredSessionId(activeSessionId)
}, [activeSessionId])
const loadSessionMessages = useCallback(async (sessionId: string) => {
const detail = await getSessionHistory(sessionId)
const fallbackTime = detail.updated
return detail.messages.map((m, i) => ({
id: `hist-${i}-${Date.now()}`,
role: m.role as "user" | "assistant",
content: m.content,
timestamp: fallbackTime,
}))
}, [])
useEffect(() => {
const storedSessionId = readStoredSessionId()
if (!storedSessionId) {
return
}
const restoreRevision = messagesRevisionRef.current
let cancelled = false
void loadSessionMessages(storedSessionId)
.then((historyMessages) => {
if (cancelled) {
return
}
if (activeSessionIdRef.current !== storedSessionId) {
return
}
if (messagesRevisionRef.current !== restoreRevision) {
return
}
setTrackedMessages(historyMessages)
setIsTyping(false)
})
.catch((err) => {
console.error("Failed to restore last session history:", err)
if (cancelled) {
return
}
if (activeSessionIdRef.current !== storedSessionId) {
return
}
if (messagesRevisionRef.current !== restoreRevision) {
return
}
localStorage.removeItem(LAST_SESSION_STORAGE_KEY)
setTrackedMessages([])
setIsTyping(false)
})
return () => {
cancelled = true
}
}, [loadSessionMessages, setTrackedMessages])
const handlePicoMessage = useCallback((msg: PicoMessage) => {
const payload = msg.payload || {}
@ -134,7 +233,7 @@ export function usePicoChat() {
? normalizeUnixTimestamp(Number(msg.timestamp))
: Date.now()
setMessages((prev) => [
setTrackedMessages((prev) => [
...prev,
{
id: messageId,
@ -152,7 +251,7 @@ export function usePicoChat() {
const messageId = payload.message_id as string
if (!messageId) break
setMessages((prev) =>
setTrackedMessages((prev) =>
prev.map((m) => (m.id === messageId ? { ...m, content } : m)),
)
break
@ -178,7 +277,7 @@ export function usePicoChat() {
default:
console.log("Unknown pico message type:", msg.type)
}
}, [])
}, [setTrackedMessages])
const connect = useCallback(async () => {
if (
@ -300,7 +399,7 @@ export function usePicoChat() {
const timestampRaw = Date.now()
// Add user message to local state
setMessages((prev) => [
setTrackedMessages((prev) => [
...prev,
{ id, role: "user", content, timestamp: timestampRaw },
])
@ -315,7 +414,7 @@ export function usePicoChat() {
payload: { content },
}
wsRef.current.send(JSON.stringify(picoMsg))
}, [])
}, [setTrackedMessages])
// Switch to a historical session
const switchSession = useCallback(
@ -325,20 +424,13 @@ export function usePicoChat() {
}
try {
const detail = await getSessionHistory(sessionId)
const fallbackTime = detail.updated
const historyMessages = detail.messages.map((m, i) => ({
id: `hist-${i}-${Date.now()}`,
role: m.role as "user" | "assistant",
content: m.content,
timestamp: fallbackTime,
}))
const historyMessages = await loadSessionMessages(sessionId)
// Only switch the active websocket session after history has loaded successfully.
disconnect()
setActiveSessionId(sessionId)
setIsTyping(false)
setMessages(historyMessages)
setTrackedMessages(historyMessages)
} catch (err) {
console.error("Failed to load session history:", err)
toast.error(t("chat.historyOpenFailed"))
@ -351,7 +443,7 @@ export function usePicoChat() {
}
}, 100)
},
[connect, disconnect, gatewayState, t],
[connect, disconnect, gatewayState, loadSessionMessages, setTrackedMessages, t],
)
// Start a new empty chat
@ -363,7 +455,7 @@ export function usePicoChat() {
disconnect()
const newId = generateSessionId()
setActiveSessionId(newId)
setMessages([])
setTrackedMessages([])
setIsTyping(false)
// Reconnect with the fresh session
@ -372,7 +464,7 @@ export function usePicoChat() {
connect()
}
}, 100)
}, [disconnect, connect, gatewayState, messages.length])
}, [disconnect, connect, gatewayState, messages.length, setTrackedMessages])
return {
messages,