Skip to content
Open
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
5 changes: 3 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
- **Chat with your library** - Ask questions about the current book or selected passages, with answers grounded in your reading context
- **Find ideas semantically** - Hybrid vector retrieval + BM25 search helps you find concepts even when the exact keyword is missing
- **Keep knowledge private** - Local embeddings and a local vector store keep your books, highlights, and notes offline-capable
- **Use your preferred model** - Connect OpenAI, Claude, Gemini, Ollama, DeepSeek, or custom-compatible providers
- **Use your preferred model** - Connect OpenAI, Claude, Gemini, Ollama, DeepSeek, OrcaRouter, or custom-compatible providers

## Why ReadAny?

Expand Down Expand Up @@ -122,7 +122,7 @@
- **Intelligent Chat** - Ask questions about your books, AI knows your position, selected text, and highlights
- **Semantic Search** - Beyond keywords, vector retrieval + BM25 hybrid search
- **Instant Translation** - AI translation or DeepL, 19 languages supported
- **Multiple AI Providers** - OpenAI, Claude, Gemini, Ollama, DeepSeek
- **Multiple AI Providers** - OpenAI, Claude, Gemini, Ollama, DeepSeek, OrcaRouter
- **Skills System** - Built-in skills (summarizer, concept explainer, character tracker, etc.) + create custom skills

### 📝 Annotation & Knowledge Management
Expand Down Expand Up @@ -238,6 +238,7 @@ Mobile app source lives in [`packages/app-expo`](packages/app-expo).
| Anthropic Claude | [console.anthropic.com](https://console.anthropic.com/) |
| Google Gemini | [aistudio.google.com](https://aistudio.google.com/) |
| Ollama / DeepSeek | Local or custom endpoint |
| OrcaRouter | [orcarouter.ai](https://www.orcarouter.ai/) |

---

Expand Down
3 changes: 2 additions & 1 deletion README_CN.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@
- **和书库对话** - 围绕当前书籍、选中文本和阅读位置提问,答案基于你的阅读上下文生成
- **按语义找内容** - 向量检索 + BM25 混合搜索,不记得原文关键词也能找回相关概念
- **知识留在本地** - 本地 embeddings 与本地向量库,让书籍、高亮和笔记具备离线可用能力
- **接入常用模型** - 支持 OpenAI、Claude、Gemini、Ollama、DeepSeek 以及兼容接口
- **接入常用模型** - 支持 OpenAI、Claude、Gemini、Ollama、DeepSeek、OrcaRouter 以及兼容接口

## 为什么选择 ReadAny?

Expand Down Expand Up @@ -237,6 +237,7 @@ Expo Go。Expo Go 无法加载 ReadAny 当前依赖的原生模块和应用配
| Anthropic Claude | [console.anthropic.com](https://console.anthropic.com/) |
| Google Gemini | [aistudio.google.com](https://aistudio.google.com/) |
| Ollama / DeepSeek | 本地或自定义端点 |
| OrcaRouter | [orcarouter.ai](https://www.orcarouter.ai/) |

---

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ const PROVIDER_OPTIONS: { id: AIProviderType; name: string }[] = [
{ id: "lmstudio", name: "LM Studio" },
{ id: "openrouter", name: "OpenRouter" },
{ id: "siliconflow", name: "SiliconFlow" },
{ id: "orcarouter", name: "OrcaRouter" },
{ id: "custom", name: "Custom" },
];

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ const PROVIDERS: { id: AIProviderType; label: string }[] = [
{ id: "lmstudio", label: "LM Studio" },
{ id: "openrouter", label: "OpenRouter" },
{ id: "siliconflow", label: "SiliconFlow" },
{ id: "orcarouter", label: "OrcaRouter" },
{ id: "moonshot", label: "Moonshot" },
{ id: "zhipu", label: "智谱 GLM" },
{ id: "aliyun", label: "阿里云通义" },
Expand Down
1 change: 1 addition & 0 deletions packages/app/src/components/onboarding/steps/AIPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ const PROVIDER_OPTIONS: { id: AIProviderType; name: string }[] = [
{ id: "lmstudio", name: "LM Studio" },
{ id: "openrouter", name: "OpenRouter" },
{ id: "siliconflow", name: "SiliconFlow" },
{ id: "orcarouter", name: "OrcaRouter" },
{ id: "custom", name: "Custom" },
];

Expand Down
1 change: 1 addition & 0 deletions packages/app/src/components/settings/AISettings.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ function useProviderOptions(): { value: AIProviderType; label: string }[] {
{ value: "lmstudio", label: "LM Studio" },
{ value: "openrouter", label: "OpenRouter" },
{ value: "siliconflow", label: "SiliconFlow" },
{ value: "orcarouter", label: "OrcaRouter" },
{ value: "moonshot", label: "Moonshot (Kimi)" },
{ value: "zhipu", label: t("settings.ai_provider_zhipu") },
{ value: "aliyun", label: t("settings.ai_provider_aliyun") },
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/types/chat.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,7 @@ export type AIProviderType =
| "mistral"
| "perplexity"
| "aihubmix"
| "orcarouter"
| "custom";

export interface AIEndpoint {
Expand Down
9 changes: 9 additions & 0 deletions packages/core/src/utils/api.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,15 @@ describe("AI API URL helpers", () => {
expect(detectProviderFromUrl("https://api.atlascloud.ai/v1")).toBe("atlascloud");
});

it("supports OrcaRouter as a named OpenAI-compatible provider", () => {
expect(getDefaultBaseUrl("orcarouter")).toBe("https://api.orcarouter.ai/v1");
expect(resolveProviderBaseUrl("orcarouter")).toBe("https://api.orcarouter.ai/v1");
expect(detectProviderFromUrl("https://api.orcarouter.ai/v1")).toBe("orcarouter");
expect(buildProviderModelsUrl("orcarouter")).toBe(
"https://api.orcarouter.ai/v1/models",
);
});

describe("ensureUrlProtocol / scheme-less inputs", () => {
it("prepends https:// when a remote URL has no scheme", () => {
expect(ensureUrlProtocol("api.openai.com/v1")).toBe("https://api.openai.com/v1");
Expand Down
9 changes: 9 additions & 0 deletions packages/core/src/utils/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,14 @@ export const PROVIDER_CONFIGS: Record<string, ProviderConfig> = {
placeholder: "https://api.siliconflow.cn",
keyPlaceholder: "sk-...",
},
orcarouter: {
id: "orcarouter",
name: "OrcaRouter",
defaultBaseUrl: "https://api.orcarouter.ai/v1",
needsV1Suffix: false,
placeholder: "https://api.orcarouter.ai/v1",
keyPlaceholder: "sk-orca-...",
},
moonshot: {
id: "moonshot",
name: "Moonshot (Kimi)",
Expand Down Expand Up @@ -566,6 +574,7 @@ export function detectProviderFromUrl(url: string): string {
if (urlLower.includes("mistral.ai")) return "mistral";
if (urlLower.includes("perplexity.ai")) return "perplexity";
if (urlLower.includes("aihubmix.com")) return "aihubmix";
if (urlLower.includes("api.orcarouter.ai")) return "orcarouter";

return "custom";
}
Expand Down