@weisiren000/oiiai 0.1.4 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 oiiai
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -9,9 +9,9 @@
9
9
  [![License](https://img.shields.io/badge/License-MIT-yellow?style=flat-square)](LICENSE)
10
10
  [![npm](https://img.shields.io/npm/v/@weisiren000/oiiai?style=flat-square&color=CB3837&logo=npm)](https://www.npmjs.com/package/@weisiren000/oiiai)
11
11
 
12
- 支持 **OpenRouter** · **Gemini** · **Groq** · **HuggingFace** · **ModelScope**
12
+ 支持 **OpenRouter** · **Gemini** · **Groq** · **HuggingFace** · **ModelScope** · **DeepSeek** · **Poe** · **Nova**
13
13
 
14
- [📖 详细文档](./docs/providers.md) · [🚀 快速开始](#快速开始) · [💡 示例](#使用示例)
14
+ [📖 详细文档](./docs/providers.md) · [🚀 快速开始](#快速开始) · [💡 示例](#使用示例) · [🔗 Fluent API](#-fluent-api-新)
15
15
 
16
16
  </div>
17
17
 
@@ -20,10 +20,13 @@
20
20
  ## ✨ 特性
21
21
 
22
22
  - 🔌 **统一接口** - 所有 Provider 使用相同 API,学会一个就会全部
23
+ - 🎯 **Fluent API** - 全新链式调用和预设实例,极简代码完成 AI 调用
23
24
  - 🧠 **Reasoning 支持** - 统一的思考模式配置,自动转换各 Provider 格式
24
25
  - 🌊 **流式输出** - 支持实时流式响应,区分思考/回答内容
26
+ - 💬 **多轮对话** - 内置对话会话管理,自动维护上下文
25
27
  - 📦 **TypeScript** - 完整类型定义,开发体验友好
26
28
  - 🔧 **可扩展** - 轻松实现自定义 Provider
29
+ - 🧪 **智能模型检测** - 自动识别思考模型,智能降级处理
27
30
 
28
31
  ## 📦 安装
29
32
 
@@ -33,16 +36,188 @@ npm install @weisiren000/oiiai
33
36
 
34
37
  ## 🚀 快速开始
35
38
 
39
+ ### 方式一:Fluent API(推荐 ⭐)
40
+
41
+ 最简洁的使用方式,支持预设实例和链式调用:
42
+
36
43
  ```typescript
37
- import { ai } from '@weisiren000/oiiai';
44
+ import { deepseek, openrouter, oiiai } from '@weisiren000/oiiai';
45
+
46
+ // 🎯 预设实例 - 一行代码完成调用
47
+ deepseek.configure({ apiKey: 'your-key' });
48
+ const answer = await deepseek.ask('deepseek-chat', '你好');
49
+
50
+ // 🔗 链式调用 - 灵活配置
51
+ const answer = await oiiai
52
+ .use('deepseek')
53
+ .key('your-key')
54
+ .model('deepseek-chat')
55
+ .system('你是一个友好的助手')
56
+ .temperature(0.7)
57
+ .ask('你好');
58
+
59
+ // 🌊 流式输出
60
+ for await (const chunk of deepseek.stream('deepseek-chat', '写首诗')) {
61
+ process.stdout.write(chunk.text);
62
+ }
63
+
64
+ // 💬 多轮对话
65
+ const chat = deepseek.chat('deepseek-chat');
66
+ await chat.send('你好');
67
+ await chat.send('继续上面的话题');
68
+ ```
69
+
70
+ ### 方式二:工厂函数
71
+
72
+ 传统方式,适合需要更多控制的场景:
73
+
74
+ ```typescript
75
+ import { ai, createProvider } from '@weisiren000/oiiai';
76
+
77
+ // 快捷工厂
78
+ const provider = ai.deepseek('your-api-key');
79
+ const answer = await provider.ask('deepseek-chat', '你好');
80
+
81
+ // 完整配置
82
+ const provider = createProvider({
83
+ provider: 'openrouter',
84
+ apiKey: 'your-api-key',
85
+ baseUrl: 'https://custom.openrouter.ai/api/v1', // 可选
86
+ });
87
+ ```
88
+
89
+ ## 🎯 Fluent API(新)
90
+
91
+ 全新的 Fluent API 提供两种使用模式:
38
92
 
39
- // 创建 Provider(所有 Provider 使用方式完全一致)
40
- const openrouter = ai.openrouter('your-api-key');
41
- const gemini = ai.gemini('your-api-key');
42
- const groq = ai.groq('your-api-key');
93
+ ### 预设实例
94
+
95
+ 预配置的 Provider 实例,开箱即用:
96
+
97
+ ```typescript
98
+ import { deepseek, openrouter, gemini, groq } from '@weisiren000/oiiai';
99
+
100
+ // 配置 API Key(二选一)
101
+ deepseek.configure({ apiKey: 'your-key' }); // 显式配置
102
+ openrouter.fromEnv(); // 从环境变量读取 (OPENROUTER_API_KEY)
43
103
 
44
104
  // 简单问答
45
- const answer = await openrouter.ask('openai/gpt-4o', '你好');
105
+ const answer = await deepseek.ask('deepseek-chat', '什么是 TypeScript?');
106
+
107
+ // 带选项的问答
108
+ const answer = await deepseek.ask('deepseek-chat', '解释量子计算', {
109
+ system: '你是一个科学家',
110
+ temperature: 0.7,
111
+ maxTokens: 1000,
112
+ reasoning: { effort: 'high' },
113
+ });
114
+
115
+ // 流式输出
116
+ for await (const chunk of deepseek.stream('deepseek-chat', '写一首诗')) {
117
+ if (chunk.type === 'reasoning') {
118
+ console.log('[思考]', chunk.text);
119
+ } else {
120
+ process.stdout.write(chunk.text);
121
+ }
122
+ }
123
+
124
+ // 带回调的流式输出
125
+ await deepseek.streamWithCallbacks('deepseek-chat', '分析这个问题', {
126
+ onReasoning: text => console.log('[思考]', text),
127
+ onContent: text => process.stdout.write(text),
128
+ onDone: result => console.log('\n完成!', result),
129
+ });
130
+ ```
131
+
132
+ ### 链式构建器
133
+
134
+ 灵活的链式调用,精细控制每个参数:
135
+
136
+ ```typescript
137
+ import { oiiai } from '@weisiren000/oiiai';
138
+
139
+ // 基础用法
140
+ const answer = await oiiai
141
+ .use('deepseek')
142
+ .key('your-key')
143
+ .model('deepseek-chat')
144
+ .ask('你好');
145
+
146
+ // 完整配置
147
+ const answer = await oiiai
148
+ .use('openrouter')
149
+ .key('your-key')
150
+ .model('anthropic/claude-sonnet-4')
151
+ .system('你是一个专业的代码助手')
152
+ .temperature(0.7)
153
+ .maxTokens(2000)
154
+ .reasoning({ effort: 'high' })
155
+ .ask('如何实现快速排序?');
156
+
157
+ // 流式输出
158
+ const stream = oiiai
159
+ .use('gemini')
160
+ .key('your-key')
161
+ .model('gemini-2.5-flash')
162
+ .stream()
163
+ .ask('写一个故事');
164
+
165
+ for await (const chunk of stream) {
166
+ process.stdout.write(chunk.text);
167
+ }
168
+ ```
169
+
170
+ ### 预设实例与构建器互转
171
+
172
+ 从简单开始,需要时再增加复杂度:
173
+
174
+ ```typescript
175
+ import { deepseek } from '@weisiren000/oiiai';
176
+
177
+ deepseek.configure({ apiKey: 'your-key' });
178
+
179
+ // 从预设实例获取构建器
180
+ const builder = deepseek.builder('deepseek-chat');
181
+
182
+ // 继续链式配置
183
+ const answer = await builder
184
+ .system('你是一个诗人')
185
+ .temperature(0.9)
186
+ .ask('写一首关于春天的诗');
187
+ ```
188
+
189
+ ### 多轮对话
190
+
191
+ 内置对话会话管理,自动维护上下文:
192
+
193
+ ```typescript
194
+ import { deepseek } from '@weisiren000/oiiai';
195
+
196
+ deepseek.configure({ apiKey: 'your-key' });
197
+
198
+ // 创建对话会话
199
+ const chat = deepseek.chat('deepseek-chat', {
200
+ system: '你是一个友好的助手',
201
+ temperature: 0.7,
202
+ });
203
+
204
+ // 多轮对话
205
+ const reply1 = await chat.send('你好,我叫小明');
206
+ console.log(reply1); // "你好小明!很高兴认识你..."
207
+
208
+ const reply2 = await chat.send('我叫什么名字?');
209
+ console.log(reply2); // "你叫小明..."
210
+
211
+ // 流式对话
212
+ for await (const chunk of chat.sendStream('给我讲个笑话')) {
213
+ process.stdout.write(chunk.text);
214
+ }
215
+
216
+ // 查看对话历史
217
+ console.log(chat.getHistory());
218
+
219
+ // 清空历史开始新对话
220
+ chat.clearHistory();
46
221
  ```
47
222
 
48
223
  ## 💡 使用示例
@@ -112,17 +287,24 @@ console.log('最终答案:', result.content);
112
287
 
113
288
  ## 🔧 支持的 Provider
114
289
 
115
- | Provider | 服务商 | Reasoning 参数 | 参考文档 |
116
- | ------------- | ------------- | ---------------------------- | ---------------------------------------------------------------------- |
117
- | `openrouter` | OpenRouter | `reasoning.effort` | [Docs](https://openrouter.ai/docs/requests) |
118
- | `gemini` | Google Gemini | `reasoning_effort` | [Docs](https://ai.google.dev/gemini-api/docs/text-generation) |
119
- | `groq` | Groq | `reasoning_format: 'parsed'` | [Docs](https://console.groq.com/docs/reasoning) |
120
- | `huggingface` | HuggingFace | 不支持 | - |
121
- | `modelscope` | 魔搭社区 | `enable_thinking` | [Docs](https://www.alibabacloud.com/help/en/model-studio/deepseek-api) |
290
+ | Provider | 服务商 | Reasoning 参数 | 支持思考模型 | 环境变量 |
291
+ | ------------- | ------------- | ---------------------------- | ------------ | --------------------- |
292
+ | `deepseek` | DeepSeek | `thinking.type: 'enabled'` | ✅ | `DEEPSEEK_API_KEY` |
293
+ | `openrouter` | OpenRouter | `reasoning.effort` | | `OPENROUTER_API_KEY` |
294
+ | `gemini` | Google Gemini | `reasoning_effort` | | `GEMINI_API_KEY` |
295
+ | `groq` | Groq | `reasoning_format: 'parsed'` | | `GROQ_API_KEY` |
296
+ | `huggingface` | HuggingFace | 取决于具体模型 | ⚠️ 部分模型 | `HUGGINGFACE_API_KEY` |
297
+ | `modelscope` | 魔搭社区 | `enable_thinking` | ✅ | `MODELSCOPE_API_KEY` |
298
+ | `poe` | Poe | `reasoning_effort` | ✅ | `POE_API_KEY` |
299
+ | `nova` | AWS Nova | `reasoningConfig.type` | ✅ | `NOVA_API_KEY` |
122
300
 
123
301
  ## 📝 常用模型
124
302
 
125
303
  ```typescript
304
+ // DeepSeek
305
+ 'deepseek-chat'; // DeepSeek-V3 通用对话
306
+ 'deepseek-reasoner'; // DeepSeek-R1 推理模型
307
+
126
308
  // OpenRouter
127
309
  'openai/gpt-4o';
128
310
  'anthropic/claude-sonnet-4';
@@ -134,32 +316,182 @@ console.log('最终答案:', result.content);
134
316
 
135
317
  // Groq
136
318
  'llama-3.3-70b-versatile';
137
- 'qwen/qwen3-32b';
319
+ 'mixtral-8x7b-32768';
138
320
 
139
- // ModelScope
140
- 'deepseek-ai/DeepSeek-R1';
141
- 'Qwen/Qwen2.5-72B-Instruct';
321
+ // Poe (使用 bot 名称)
322
+ 'GPT-4o';
323
+ 'Claude-3.5-Sonnet';
324
+
325
+ // AWS Nova
326
+ 'nova-2-lite-v1';
327
+ 'nova-2-v1';
328
+ 'nova-premier-v1';
142
329
  ```
143
330
 
144
- ## 🛠️ 自定义 Provider
331
+ ## 🌍 环境变量
332
+
333
+ 推荐使用 `.env` 文件管理 API 密钥:
334
+
335
+ ```bash
336
+ # .env
337
+ DEEPSEEK_API_KEY=sk-xxx
338
+ OPENROUTER_API_KEY=sk-or-xxx
339
+ GEMINI_API_KEY=xxx
340
+ GROQ_API_KEY=gsk_xxx
341
+ HUGGINGFACE_API_KEY=hf_xxx
342
+ MODELSCOPE_API_KEY=xxx
343
+ POE_API_KEY=xxx
344
+ NOVA_API_KEY=xxx
345
+ ```
346
+
347
+ ```typescript
348
+ import 'dotenv/config';
349
+ import { deepseek, openrouter } from '@weisiren000/oiiai';
350
+
351
+ // 从环境变量读取配置
352
+ deepseek.fromEnv();
353
+ openrouter.fromEnv();
354
+
355
+ const answer = await deepseek.ask('deepseek-chat', '你好');
356
+ ```
357
+
358
+ ## 🧪 高级用法
359
+
360
+ ### 智能模型检测与降级
361
+
362
+ ```typescript
363
+ import { ModelDetection, detectByModelName } from '@weisiren000/oiiai';
364
+
365
+ // 基于模型名称模式检测
366
+ const characteristics = detectByModelName('deepseek-r1');
367
+ console.log(characteristics.behavior); // 'thinking-first'
368
+
369
+ // 自定义降级策略
370
+ provider.configureFallback({
371
+ enabled: true,
372
+ returnReasoningAsContent: true,
373
+ extractConclusionFromReasoning: true,
374
+ });
375
+ ```
376
+
377
+ ### 场景化问答
145
378
 
146
379
  ```typescript
147
- import { BaseProvider } from '@weisiren000/oiiai';
148
- import type { ChatOptions, ChatResult, StreamChunk } from '@weisiren000/oiiai';
380
+ const answer = await provider.askWithScenario(
381
+ 'deepseek-r1',
382
+ '证明勾股定理',
383
+ 'math' // 'simple' | 'math' | 'reasoning' | 'fast'
384
+ );
385
+ ```
386
+
387
+ ## 🛠️ 自定义 Provider
149
388
 
150
- class MyProvider extends BaseProvider {
389
+ ### 使用适配器模式(推荐)
390
+
391
+ ```typescript
392
+ import {
393
+ BaseAdapter,
394
+ ProviderRegistry,
395
+ RequestBuilder,
396
+ StreamProcessor,
397
+ } from '@weisiren000/oiiai';
398
+
399
+ class MyAdapter extends BaseAdapter {
151
400
  readonly name = 'my-provider';
152
401
 
153
- async chat(options: ChatOptions): Promise<ChatResult> {
154
- // 实现非流式请求
402
+ getEndpointUrl(baseUrl: string): string {
403
+ return `${baseUrl}/v1/chat/completions`;
404
+ }
405
+
406
+ buildChatRequest(options: ChatOptions): Record<string, unknown> {
407
+ return RequestBuilder.buildChatBody(options);
408
+ }
409
+
410
+ parseChatResponse(
411
+ response: Record<string, unknown>,
412
+ model: string
413
+ ): ChatResult {
414
+ // 解析响应
415
+ }
416
+
417
+ extractStreamChunk(delta: Record<string, unknown>): StreamChunk | null {
418
+ // 提取流式数据块
155
419
  }
420
+ }
421
+
422
+ // 注册并使用
423
+ ProviderRegistry.register(new MyAdapter());
424
+ const provider = createProvider({ provider: 'my-provider', apiKey: 'xxx' });
425
+ ```
426
+
427
+ ## 🏗️ 架构概览
428
+
429
+ ```
430
+ ┌─────────────────────────────────────────────────────────┐
431
+ │ Fluent API (新) │
432
+ │ 预设实例 / 链式构建器 / 对话会话 │
433
+ ├─────────────────────────────────────────────────────────┤
434
+ │ 用户层 (API) │
435
+ │ createProvider / ai 快捷函数 │
436
+ ├─────────────────────────────────────────────────────────┤
437
+ │ 注册层 (Registry) │
438
+ │ ProviderRegistry │
439
+ ├─────────────────────────────────────────────────────────┤
440
+ │ 适配器层 (Adapter) │
441
+ │ OpenRouterAdapter / GeminiAdapter / GroqAdapter ... │
442
+ ├─────────────────────────────────────────────────────────┤
443
+ │ 客户端层 (Client) │
444
+ │ HttpProviderClient │
445
+ ├─────────────────────────────────────────────────────────┤
446
+ │ 工具层 (Utils) │
447
+ │ StreamProcessor / RequestBuilder / ConfigValidator │
448
+ └─────────────────────────────────────────────────────────┘
449
+ ```
450
+
451
+ ## ❓ 故障排除
452
+
453
+ ### 常见问题
454
+
455
+ **Q: 为什么某些思考模型返回的 `content` 为空?**
456
+ A: 思考模型在低 token 限制下可能只输出 reasoning。启用智能降级策略或增加 `maxTokens` 即可解决。
457
+
458
+ **Q: 如何自定义 API 地址?**
459
+ A: 使用 `baseUrl` 参数:
460
+
461
+ ```typescript
462
+ // Fluent API
463
+ deepseek.configure({ apiKey: 'xxx', baseUrl: 'https://custom.api.com' });
156
464
 
157
- async *chatStream(options: ChatOptions): AsyncGenerator<StreamChunk> {
158
- // 实现流式请求
465
+ // 或链式调用
466
+ oiiai.use('deepseek').key('xxx').baseUrl('https://custom.api.com');
467
+ ```
468
+
469
+ **Q: 流式输出中如何区分思考和回答?**
470
+ A: 检查 `chunk.type`:`'reasoning'` 表示思考内容,`'content'` 表示最终回答。
471
+
472
+ ### 错误处理
473
+
474
+ ```typescript
475
+ import { ConfigurationError, APIError } from '@weisiren000/oiiai';
476
+
477
+ try {
478
+ const answer = await deepseek.ask('model', 'question');
479
+ } catch (error) {
480
+ if (error instanceof ConfigurationError) {
481
+ console.error('配置错误:', error.message);
482
+ } else if (error instanceof APIError) {
483
+ console.error('API 错误:', error.message);
159
484
  }
160
485
  }
161
486
  ```
162
487
 
488
+ ## 📚 更多文档
489
+
490
+ - [API 参考](./docs/API.md) - 完整的 API 文档
491
+ - [Provider 指南](./docs/providers.md) - 各 Provider 详细说明
492
+ - [快速上手指南](./docs/quickstart.md) - 新手入门教程
493
+ - [贡献指南](./CONTRIBUTING.md) - 如何参与贡献
494
+
163
495
  ## 📄 License
164
496
 
165
497
  MIT