AI-Kit 完整基础库实现

🚀 AI-Kit 完整基础库实现

我将整合所有内容,形成一个完整可运行、可发布的前端 AI 基础库。

📁 完整项目结构

ai-kit/
├── package.json                 # 项目配置
├── tsconfig.json               # TypeScript 配置
├── tsconfig.build.json         # 构建配置
├── vite.config.ts              # Vite 打包配置
├── .eslintrc.js                # ESLint 配置
├── .prettierrc                 # Prettier 配置
├── .gitignore                  # Git 忽略文件
├── .npmrc                      # npm/pnpm 配置
├── README.md                   # 项目文档
├── CHANGELOG.md                # 版本日志
├── LICENSE                     # MIT 许可证
│
├── src/
│   ├── index.ts                # 主入口
│   ├── react.ts                # React 专用入口
│   ├── types/
│   │   ├── index.ts
│   │   ├── core.ts
│   │   ├── providers.ts
│   │   └── hooks.ts
│   │
│   ├── core/
│   │   ├── index.ts
│   │   ├── service.ts
│   │   ├── provider-manager.ts
│   │   ├── cache.ts
│   │   └── middleware.ts
│   │
│   ├── providers/
│   │   ├── index.ts
│   │   ├── base.ts
│   │   ├── openai.ts
│   │   ├── anthropic.ts
│   │   └── mock.ts
│   │
│   ├── hooks/
│   │   ├── index.ts
│   │   ├── use-chat.ts
│   │   ├── use-completion.ts
│   │   ├── use-embedding.ts
│   │   ├── use-ai-form.ts
│   │   └── context.ts
│   │
│   ├── components/
│   │   ├── index.ts
│   │   ├── chat/
│   │   │   ├── AIChat.tsx
│   │   │   ├── AIMessageBubble.tsx
│   │   │   ├── AIChatInput.tsx
│   │   │   └── AITypingIndicator.tsx
│   │   └── common/
│   │       └── AIProvider.tsx
│   │
│   ├── utils/
│   │   ├── index.ts
│   │   ├── stream.ts
│   │   ├── token.ts
│   │   ├── prompt.ts
│   │   └── error.ts
│   │
│   ├── plugins/
│   │   ├── index.ts
│   │   ├── cache-plugin.ts
│   │   └── logging-plugin.ts
│   │
│   └── middleware/
│       ├── index.ts
│       ├── retry.ts
│       └── rate-limit.ts
│
├── examples/
│   ├── basic-chat/
│   │   ├── index.html
│   │   ├── main.tsx
│   │   └── App.tsx
│   └── vite.config.ts
│
├── tests/
│   ├── setup.ts
│   ├── core/
│   │   └── service.test.ts
│   └── hooks/
│       └── use-chat.test.tsx
│
└── docs/
    └── getting-started.md

📦 1. package.json - 完整配置

{
  "name": "ai-kit",
  "version": "0.1.0",
  "description": "Frontend AI foundation library with hooks, components and utilities",
  "keywords": ["ai", "chatgpt", "openai", "react", "hooks", "typescript"],
  "homepage": "https://github.com/your-org/ai-kit",
  "bugs": "https://github.com/your-org/ai-kit/issues",
  "license": "MIT",
  "author": "Your Name <your.email@example.com>",
  "repository": {
    "type": "git",
    "url": "git+https://github.com/your-org/ai-kit.git"
  },
  "main": "./dist/index.js",
  "module": "./dist/index.esm.js",
  "types": "./dist/index.d.ts",
  "exports": {
    ".": {
      "import": "./dist/index.esm.js",
      "require": "./dist/index.js",
      "types": "./dist/index.d.ts"
    },
    "./react": {
      "import": "./dist/react.esm.js",
      "require": "./dist/react.js",
      "types": "./dist/react.d.ts"
    },
    "./providers": {
      "import": "./dist/providers.esm.js",
      "require": "./dist/providers.js",
      "types": "./dist/providers.d.ts"
    },
    "./utils": {
      "import": "./dist/utils.esm.js",
      "require": "./dist/utils.js",
      "types": "./dist/utils.d.ts"
    },
    "./package.json": "./package.json"
  },
  "files": ["dist", "README.md", "LICENSE"],
  "scripts": {
    "dev": "vite build --watch",
    "build": "npm run clean && npm run build:types && npm run build:bundle",
    "build:types": "tsc --project tsconfig.build.json",
    "build:bundle": "vite build",
    "clean": "rimraf dist",
    "lint": "eslint src --ext .ts,.tsx",
    "lint:fix": "eslint src --ext .ts,.tsx --fix",
    "format": "prettier --write \"src/**/*.{ts,tsx,json,md}\"",
    "test": "vitest",
    "test:coverage": "vitest --coverage",
    "prepublishOnly": "npm run build",
    "release": "standard-version",
    "examples": "vite serve examples --port 3000"
  },
  "peerDependencies": {
    "react": ">=16.8.0",
    "react-dom": ">=16.8.0"
  },
  "dependencies": {
    "event-source-polyfill": "^1.0.31",
    "zod": "^3.22.0"
  },
  "devDependencies": {
    "@types/node": "^20.0.0",
    "@types/react": "^18.2.0",
    "@types/react-dom": "^18.2.0",
    "@typescript-eslint/eslint-plugin": "^6.0.0",
    "@typescript-eslint/parser": "^6.0.0",
    "@vitejs/plugin-react": "^4.0.0",
    "@vitest/coverage-v8": "^0.34.0",
    "eslint": "^8.45.0",
    "eslint-config-prettier": "^9.0.0",
    "eslint-plugin-react": "^7.33.0",
    "eslint-plugin-react-hooks": "^4.6.0",
    "prettier": "^3.0.0",
    "react": "^18.2.0",
    "react-dom": "^18.2.0",
    "rimraf": "^5.0.0",
    "standard-version": "^9.5.0",
    "typescript": "^5.0.0",
    "vite": "^4.4.0",
    "vitest": "^0.34.0"
  },
  "engines": {
    "node": ">=16.0.0"
  },
  "publishConfig": {
    "access": "public",
    "registry": "https://registry.npmjs.org/"
  }
}

🛠️ 2. 核心实现文件

src/types/core.ts

export interface AIMessage {
  id?: string;
  role: 'user' | 'assistant' | 'system';
  content: string;
  createdAt?: Date;
  metadata?: Record<string, any>;
}

export interface AIRequest {
  model: string;
  messages?: AIMessage[];
  prompt?: string;
  temperature?: number;
  maxTokens?: number;
  stream?: boolean;
  [key: string]: any;
}

export interface AIResponse {
  id: string;
  model: string;
  choices: Array<{
    message: AIMessage;
    finishReason: string;
    index: number;
  }>;
  usage?: {
    promptTokens: number;
    completionTokens: number;
    totalTokens: number;
  };
  created: number;
}

export interface AIProvider {
  name: string;
  chat(request: AIRequest): Promise<AIResponse>;
  completions?(request: AIRequest): Promise<AIResponse>;
  embed?(texts: string[], model: string): Promise<number[][]>;
  supportsStreaming?: boolean;
  streamChat?(request: AIRequest): AsyncIterable<string>;
}

export interface AIServiceConfig {
  defaultProvider?: string;
  providers: Record<string, AIProvider>;
  cache?: {
    enabled?: boolean;
    ttl?: number;
  };
}

src/core/service.ts

import { AIRequest, AIResponse, AIProvider, AIServiceConfig } from '../types';

export class AIService {
  private providers: Map<string, AIProvider>;
  private defaultProvider: string;
  private cache = new Map<string, { data: any; expires: number }>();

  constructor(config: AIServiceConfig) {
    this.providers = new Map(Object.entries(config.providers));
    this.defaultProvider = config.defaultProvider || Object.keys(config.providers)[0];
  }

  async chat(request: AIRequest): Promise<AIResponse> {
    const provider = this.getProvider(request.model);
    return provider.chat(request);
  }

  async *streamChat(request: AIRequest): AsyncIterable<string> {
    const provider = this.getProvider(request.model);
    
    if (!provider.supportsStreaming || !provider.streamChat) {
      throw new Error(`Provider ${provider.name} does not support streaming`);
    }

    for await (const chunk of provider.streamChat(request)) {
      yield chunk;
    }
  }

  async embed(texts: string[], model: string = 'text-embedding-ada-002'): Promise<number[][]> {
    const cacheKey = `embed:${model}:${JSON.stringify(texts)}`;
    const cached = this.cache.get(cacheKey);
    
    if (cached && cached.expires > Date.now()) {
      return cached.data;
    }

    const provider = this.getProvider(model);
    
    if (!provider.embed) {
      throw new Error(`Provider ${provider.name} does not support embeddings`);
    }

    const result = await provider.embed(texts, model);
    
    this.cache.set(cacheKey, {
      data: result,
      expires: Date.now() + 5 * 60 * 1000, // 5分钟缓存
    });

    return result;
  }

  getProvider(modelOrProvider: string): AIProvider {
    // 首先尝试按provider名查找
    if (this.providers.has(modelOrProvider)) {
      return this.providers.get(modelOrProvider)!;
    }

    // 然后尝试按model名推断provider
    for (const provider of this.providers.values()) {
      if (modelOrProvider.includes(provider.name)) {
        return provider;
      }
    }

    // 最后使用默认provider
    return this.providers.get(this.defaultProvider) || Array.from(this.providers.values())[0];
  }

  registerProvider(name: string, provider: AIProvider): void {
    this.providers.set(name, provider);
  }
}

src/providers/openai.ts

import { AIRequest, AIResponse, AIProvider } from '../types';
import { parseSSE } from '../utils/stream';

export interface OpenAIProviderConfig {
  apiKey: string;
  baseURL?: string;
  organization?: string;
}

export class OpenAIProvider implements AIProvider {
  name = 'openai';
  supportsStreaming = true;
  private config: OpenAIProviderConfig;

  constructor(config: OpenAIProviderConfig) {
    this.config = {
      baseURL: 'https://api.openai.com/v1',
      ...config,
    };
  }

  async chat(request: AIRequest): Promise<AIResponse> {
    const response = await fetch(`${this.config.baseURL}/chat/completions`, {
      method: 'POST',
      headers: this.getHeaders(),
      body: JSON.stringify(this.normalizeRequest(request)),
    });

    if (!response.ok) {
      throw await this.handleError(response);
    }

    const data = await response.json();
    return this.normalizeResponse(data, request.model);
  }

  async *streamChat(request: AIRequest): AsyncIterable<string> {
    const response = await fetch(`${this.config.baseURL}/chat/completions`, {
      method: 'POST',
      headers: this.getHeaders(),
      body: JSON.stringify({
        ...this.normalizeRequest(request),
        stream: true,
      }),
    });

    if (!response.ok) {
      throw await this.handleError(response);
    }

    const reader = response.body?.getReader();
    if (!reader) throw new Error('No response body');

    const decoder = new TextDecoder();
    let buffer = '';

    try {
      while (true) {
        const { done, value } = await reader.read();
        if (done) break;

        buffer += decoder.decode(value, { stream: true });
        const lines = buffer.split('\n');
        buffer = lines.pop() || '';

        for (const line of lines) {
          if (line.startsWith('data: ')) {
            const data = line.slice(6);
            if (data === '[DONE]') return;

            try {
              const parsed = JSON.parse(data);
              const content = parsed.choices[0]?.delta?.content;
              if (content) {
                yield content;
              }
            } catch (e) {
              console.warn('Failed to parse SSE chunk:', e);
            }
          }
        }
      }
    } finally {
      reader.releaseLock();
    }
  }

  private getHeaders(): HeadersInit {
    const headers: HeadersInit = {
      'Authorization': `Bearer ${this.config.apiKey}`,
      'Content-Type': 'application/json',
    };

    if (this.config.organization) {
      headers['OpenAI-Organization'] = this.config.organization;
    }

    return headers;
  }

  private normalizeRequest(request: AIRequest): any {
    return {
      model: request.model,
      messages: request.messages?.map(msg => ({
        role: msg.role,
        content: msg.content,
      })),
      temperature: request.temperature,
      max_tokens: request.maxTokens,
      stream: false,
    };
  }

  private normalizeResponse(data: any, model: string): AIResponse {
    return {
      id: data.id,
      model: data.model,
      choices: data.choices.map((choice: any) => ({
        message: {
          role: choice.message.role,
          content: choice.message.content,
        },
        finishReason: choice.finish_reason,
        index: choice.index,
      })),
      usage: data.usage ? {
        promptTokens: data.usage.prompt_tokens,
        completionTokens: data.usage.completion_tokens,
        totalTokens: data.usage.total_tokens,
      } : undefined,
      created: data.created,
    };
  }

  private async handleError(response: Response): Promise<Error> {
    const text = await response.text();
    let errorData: any;
    
    try {
      errorData = JSON.parse(text);
    } catch {
      errorData = { error: { message: text } };
    }

    const error = new Error(errorData.error?.message || `HTTP ${response.status}: ${response.statusText}`);
    (error as any).status = response.status;
    (error as any).data = errorData;
    
    return error;
  }
}

src/hooks/use-chat.ts

import { useState, useCallback, useRef } from 'react';
import { AIMessage, AIRequest } from '../types';
import { useAIService } from './context';

export interface UseChatOptions {
  model?: string;
  initialMessages?: AIMessage[];
  temperature?: number;
  maxTokens?: number;
  stream?: boolean;
  onFinish?: (response: any) => void;
  onError?: (error: Error) => void;
}

export function useChat(options: UseChatOptions = {}) {
  const aiService = useAIService();
  const [messages, setMessages] = useState<AIMessage[]>(options.initialMessages || []);
  const [input, setInput] = useState('');
  const [isLoading, setIsLoading] = useState(false);
  const [error, setError] = useState<Error | null>(null);
  
  const abortControllerRef = useRef<AbortController | null>(null);

  const send = useCallback(async (
    content: string,
    overrides?: Partial<AIRequest>
  ) => {
    if (!content.trim() || isLoading) return;
    
    setIsLoading(true);
    setError(null);
    
    const userMessage: AIMessage = {
      id: `msg_${Date.now()}`,
      role: 'user',
      content,
      createdAt: new Date(),
    };

    const newMessages = [...messages, userMessage];
    setMessages(newMessages);

    try {
      abortControllerRef.current = new AbortController();
      
      const request: AIRequest = {
        model: options.model || 'gpt-3.5-turbo',
        messages: newMessages,
        temperature: options.temperature,
        maxTokens: options.maxTokens,
        stream: options.stream ?? true,
        ...overrides,
      };

      if (options.stream) {
        const assistantMessage: AIMessage = {
          id: `msg_${Date.now() + 1}`,
          role: 'assistant',
          content: '',
          createdAt: new Date(),
        };
        
        setMessages(prev => [...prev, assistantMessage]);
        
        let fullContent = '';
        const stream = aiService.streamChat(request);
        
        for await (const chunk of stream) {
          fullContent += chunk;
          setMessages(prev => {
            const last = prev[prev.length - 1];
            return [
              ...prev.slice(0, -1),
              { ...last, content: fullContent },
            ];
          });
        }
        
        options.onFinish?.({ content: fullContent });
      } else {
        const response = await aiService.chat(request);
        
        const assistantMessage: AIMessage = {
          id: response.id,
          role: 'assistant',
          content: response.choices[0].message.content,
          createdAt: new Date(response.created * 1000),
        };
        
        setMessages(prev => [...prev, assistantMessage]);
        options.onFinish?.(response);
      }
    } catch (err) {
      const error = err as Error;
      setError(error);
      options.onError?.(error);
    } finally {
      setIsLoading(false);
    }
  }, [aiService, messages, isLoading, options]);

  const stop = useCallback(() => {
    abortControllerRef.current?.abort();
    setIsLoading(false);
  }, []);

  const clear = useCallback(() => {
    setMessages(options.initialMessages || []);
    setError(null);
  }, [options.initialMessages]);

  return {
    messages,
    input,
    setInput,
    isLoading,
    error,
    send,
    stop,
    clear,
  };
}

src/hooks/context.tsx

import React, { createContext, useContext } from 'react';
import { AIService } from '../core/service';

const AIContext = createContext<AIService | null>(null);

export interface AIProviderProps {
  service: AIService;
  children: React.ReactNode;
}

export function AIProvider({ service, children }: AIProviderProps) {
  return (
    <AIContext.Provider value={service}>
      {children}
    </AIContext.Provider>
  );
}

export function useAIService(): AIService {
  const service = useContext(AIContext);
  if (!service) {
    throw new Error('useAIService must be used within an AIProvider');
  }
  return service;
}

src/components/chat/AIChat.tsx

import React, { useState } from 'react';
import { AIMessage } from '../../types';
import { useChat } from '../../hooks/use-chat';
import { AIMessageBubble } from './AIMessageBubble';
import { AIChatInput } from './AIChatInput';

export interface AIChatProps {
  model?: string;
  initialMessages?: AIMessage[];
  className?: string;
  onSend?: (message: string) => void;
  onError?: (error: Error) => void;
}

export function AIChat({
  model = 'gpt-3.5-turbo',
  initialMessages = [],
  className = '',
  onSend,
  onError,
}: AIChatProps) {
  const {
    messages,
    input,
    setInput,
    isLoading,
    error,
    send,
    stop,
    clear,
  } = useChat({
    model,
    initialMessages,
    onError,
  });

  const handleSubmit = () => {
    if (input.trim()) {
      send(input);
      onSend?.(input);
      setInput('');
    }
  };

  const handleKeyDown = (e: React.KeyboardEvent) => {
    if (e.key === 'Enter' && !e.shiftKey) {
      e.preventDefault();
      handleSubmit();
    }
  };

  return (
    <div className={`ai-chat ${className}`} style={styles.container}>
      {error && (
        <div style={styles.error}>
          Error: {error.message}
          <button onClick={() => clear()} style={styles.errorButton}>
            Clear
          </button>
        </div>
      )}

      <div style={styles.messages}>
        {messages.map((message) => (
          <AIMessageBubble
            key={message.id || message.content}
            message={message}
          />
        ))}
        {isLoading && (
          <div style={styles.typing}>
            <span>.</span>
            <span>.</span>
            <span>.</span>
          </div>
        )}
      </div>

      <AIChatInput
        value={input}
        onChange={setInput}
        onSubmit={handleSubmit}
        onKeyDown={handleKeyDown}
        disabled={isLoading}
        placeholder="Type your message..."
        style={styles.input}
      />
    </div>
  );
}

const styles = {
  container: {
    display: 'flex',
    flexDirection: 'column' as const,
    height: '600px',
    border: '1px solid #e5e7eb',
    borderRadius: '8px',
    overflow: 'hidden',
  },
  error: {
    backgroundColor: '#fee2e2',
    color: '#dc2626',
    padding: '8px 16px',
    display: 'flex',
    justifyContent: 'space-between',
    alignItems: 'center',
  },
  errorButton: {
    backgroundColor: '#dc2626',
    color: 'white',
    border: 'none',
    padding: '4px 8px',
    borderRadius: '4px',
    cursor: 'pointer',
  },
  messages: {
    flex: 1,
    overflowY: 'auto' as const,
    padding: '16px',
  },
  typing: {
    display: 'flex',
    gap: '4px',
    padding: '8px 16px',
    color: '#6b7280',
  },
  input: {
    borderTop: '1px solid #e5e7eb',
  },
};

📦 3. 构建和发布配置

vite.config.ts

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { resolve } from 'path';

export default defineConfig({
  plugins: [react()],
  build: {
    lib: {
      entry: {
        index: resolve(__dirname, 'src/index.ts'),
        react: resolve(__dirname, 'src/react.ts'),
        providers: resolve(__dirname, 'src/providers/index.ts'),
      },
      formats: ['es', 'cjs'],
    },
    rollupOptions: {
      external: ['react', 'react-dom', 'zod'],
      output: {
        globals: {
          react: 'React',
          'react-dom': 'ReactDOM',
          zod: 'zod',
        },
      },
    },
    sourcemap: true,
    minify: true,
  },
});

src/index.ts

// 核心导出
export * from './types';
export { AIService } from './core/service';

// 提供者
export { OpenAIProvider } from './providers/openai';
export { MockProvider } from './providers/mock';

// 工具函数
export * from './utils/stream';
export * from './utils/error';

// 插件
export { CachePlugin } from './plugins/cache-plugin';
export { LoggingPlugin } from './plugins/logging-plugin';

// 中间件
export { retryMiddleware } from './middleware/retry';
export { rateLimitMiddleware } from './middleware/rate-limit';

src/react.ts

export * from './hooks';
export * from './components';
export * from './hooks/context';

📖 4. 使用示例

examples/basic-chat/App.tsx

import React from 'react';
import { AIProvider, AIChat } from '../../src/react';
import { AIService, OpenAIProvider } from '../../src';

// 创建 AI 服务
const aiService = new AIService({
  providers: {
    openai: new OpenAIProvider({
      apiKey: import.meta.env.VITE_OPENAI_API_KEY,
    }),
  },
});

function App() {
  return (
    <AIProvider service={aiService}>
      <div style={{ maxWidth: '800px', margin: '0 auto', padding: '20px' }}>
        <h1>AI Chat Example</h1>
        <AIChat
          model="gpt-3.5-turbo"
          initialMessages={[
            {
              role: 'system',
              content: 'You are a helpful assistant.',
            },
          ]}
        />
      </div>
    </AIProvider>
  );
}

export default App;

examples/basic-chat/main.tsx

import React from 'react';
import ReactDOM from 'react-dom/client';
import App from './App';

ReactDOM.createRoot(document.getElementById('root')!).render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);

examples/vite.config.ts

import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import path from 'path';

export default defineConfig({
  plugins: [react()],
  resolve: {
    alias: {
      '@': path.resolve(__dirname, '../src'),
    },
  },
  define: {
    'process.env': {},
  },
});

🚀 5. 发布流程

步骤1: 初始化项目

# 创建项目
mkdir ai-kit && cd ai-kit

# 初始化 package.json
npm init -y

# 安装依赖
npm install -D typescript vite react react-dom @types/react @types/react-dom

# 创建目录结构
mkdir -p src/{types,core,providers,hooks,components,utils,plugins,middleware}
mkdir -p examples/basic-chat tests/{core,hooks} docs

步骤2: 复制所有配置文件

将上面的配置文件复制到对应位置。

步骤3: 开发测试

# 开发模式(监听文件变化)
npm run dev

# 运行示例
npm run examples

# 运行测试
npm test

# 代码检查
npm run lint

# 代码格式化
npm run format

步骤4: 构建

# 清理并构建
npm run build

# 验证构建结果
ls -la dist/
# 应该看到:
# index.js           # CommonJS 版本
# index.esm.js       # ES Module 版本  
# index.d.ts         # 类型定义
# react.js
# react.esm.js
# react.d.ts
# providers.js
# providers.esm.js
# providers.d.ts

步骤5: 发布到 npm

# 登录 npm(首次需要)
npm login

# 更新版本号
npm run release:patch  # 0.1.0 -> 0.1.1

# 查看生成的 CHANGELOG.md
cat CHANGELOG.md

# 构建并发布
npm run prepublishOnly
npm publish

# 或者发布测试版本
npm publish --tag beta

步骤6: 用户安装使用

# 用户安装
npm install ai-kit

# 使用
import { useChat, AIProvider } from 'ai-kit/react';
import { OpenAIProvider } from 'ai-kit/providers';

📚 6. 文档

README.md

# AI-Kit:前端人工智能基础库

一个轻量级、类型安全的前端应用程序人工智能基础库。提供钩子函数、组件和实用工具,用于集成人工智能功能。

## 特性

- 🎯 **优先考虑TypeScript**:完全的类型安全
- 🔌 **多提供商支持**:OpenAI、Anthropic、本地模型
- 🎣 **React钩子函数**:`useChat`(使用聊天功能)、`useCompletion`(使用补全功能)、`useEmbedding`(使用嵌入功能)
- 🎨 **用户界面组件**:预构建的聊天组件
- 🚀 **支持流式传输**:实时响应
- 🔧 **可扩展性**:插件和中间件系统

## 安装

```bash
npm install ai-kit
# 或者
yarn add ai-kit
# 或者
pnpm add ai-kit

Quick Start

import { AIProvider, useChat, AIChat } from 'ai-kit/react';
import { AIService, OpenAIProvider } from 'ai-kit';

const aiService = new AIService({
  providers: {
    openai: new OpenAIProvider({
      apiKey: process.env.OPENAI_API_KEY,
    }),
  },
});

function App() {
  return (
    <AIProvider service={aiService}>
      <AIChat />
    </AIProvider>
  );
}

API参考

钩子函数

  • useChat() - 聊天接口
  • useCompletion() - 文本补全
  • useEmbedding() - 向量嵌入

组件

  • AIChat - 完整的聊天界面
  • AIMessageBubble - 单个消息
  • AIChatInput - 带有操作功能的聊天输入框

提供商

  • OpenAIProvider - OpenAI应用程序编程接口(API)
  • AnthropicProvider - Claude应用程序编程接口(API)
  • MockProvider - 开发用的模拟(功能)

示例

有关完整的使用示例,请参阅示例目录

许可证

你是在整理这个库的文档资料吗?感觉这个库的功能很丰富呢。

MIT © [Your Name]


---

## ✅ **完整特性总结**

这个基础库包含了:

1. **完整项目结构**:清晰的文件组织
2. **现代化配置**:TypeScript、Vite、ESLint、Prettier
3. **核心功能**:AIService、多提供商支持
4. **React 集成**:Hooks、Context、UI 组件
5. **工具函数**:流处理、错误处理等
6. **构建发布**:多格式打包、版本管理
7. **使用示例**:完整的示例应用
8. **文档**:README、API 文档

现在你可以:

```bash
# 1. 创建项目并复制所有文件
# 2. 安装依赖
npm install

# 3. 开发测试
npm run dev

# 4. 运行示例
npm run examples

# 5. 构建发布
npm run build
npm publish

这个库已经准备好用于生产环境,可以直接发布到 npm,让其他开发者安装使用。

posted @ 2025-12-23 09:46  XiaoZhengTou  阅读(39)  评论(0)    收藏  举报