npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

my-ai-chat-framework

v2.7.0

Published

A lightweight AI chat framework with plugin system, unified message format, and tool calling support.

Readme

🤖 My AI Chat Framework

A lightweight, modular AI chat framework with plugin system, unified message format, and tool calling support.

⚠️ Note: This project is created for learning purposes and is AI‑generated. It is not intended for production use. No backward compatibility is guaranteed. Use at your own risk.

✨ Features

  • Lightweight Core – ~300 lines, easy to understand and extend.
  • Plugin System – Add features (tool calling, reasoning, custom adapters) without touching the core.
  • Unified Message Format – Consistent data structure across all components.
  • Multi‑Environment – Builds ES module, UMD, and CommonJS for browser & Node.js.
  • No External Dependencies – Uses native fetch (Node 18+ & modern browsers).
  • Tool Calling – Built‑in plugin to handle function calls from AI models.
  • Streaming – Full support for real‑time responses.
  • Flexible Configuration – Supports both flat and nested modelParams structure.
  • Event‑Driven – Built‑in EventEmitter for message, sending, error, stream-progress events.
  • Custom Error ClassesAPIError, NetworkError, ConfigurationError, ParsingError for fine‑grained error handling.

🧱 Architecture

src/
├── index.js                  # Public entry point (re-exports)
├── core/
│   ├── ChatService.js        # Main service: config, send/stream, plugin hosting
│   ├── MessageStore.js       # In-memory message list with CRUD helpers
│   ├── EventEmitter.js       # Minimal pub/sub (on/off/emit)
│   └── Errors.js             # Custom error classes
├── adapters/
│   └── openai.js             # OpenAI‑compatible API adapter (plugin pattern)
├── plugins/
│   └── tool-calling.js       # Tool calling plugin (auto‑detect & loop)
└── utils/
    ├── typeCheck.js          # Type checking helpers
    └── url.js                # URL joining utility

Data flow:

User calls chat.send(input)
  → ChatService._request()
    → emits 'sending'
    → MessageStore.add(userMsg)
    → adapter.buildRequest(messages, config)   ← formats request body
    → adapter.send(requestBody, config)         ← HTTP call (fetch)
    → adapter.parseResponse(response)           ← normalize response
    → MessageStore.add(assistantMsg)
    → emits 'message'
    → returns assistantMsg

With toolCallingPlugin, the flow loops: response → detect tool_calls → execute tools → add tool results → sendExisting → repeat (max 5 iterations).


📦 Installation

npm install my-ai-chat-framework

🚀 Quick Start

Basic Usage (Flat Configuration)

import { ChatService, openaiAdapter, toolCallingPlugin } from 'my-ai-chat-framework';

const chat = new ChatService({
  apiKey: 'your-api-key',
  baseUrl: 'https://api.deepseek.com',    // optional, defaults to OpenAI
  model: 'deepseek-chat',
  temperature: 0.7,
  maxTokens: 2000
});

chat.use(openaiAdapter);
chat.use(toolCallingPlugin);

chat.on('message', msg => console.log(msg.content));
await chat.send('Hello!');

Using modelParams (Recommended for Many Parameters)

const chat = new ChatService({
  apiKey: 'your-api-key',
  baseUrl: 'https://api.deepseek.com',
  model: 'deepseek-chat',               // still at top level for convenience
  modelParams: {                         // optional parameters grouped
    temperature: 0.8,
    maxTokens: 1500,
    reasoningEffort: 'medium'           // for deepseek-reasoner
  }
});

Registering a Tool

chat.registerTool('get_weather', 'Get current weather for a city',
  async (args) => {
    // args = { city: 'Beijing' }
    return `Weather in ${args.city}: 22°C, sunny`;
  },
  {  // parameter schema (optional but recommended)
    city: { type: 'string', description: 'City name', required: true }
  }
);

await chat.send('What\'s the weather in Beijing?');
// → AI calls get_weather, framework executes it, AI responds with weather info

🔌 Plugins & Adapters

openaiAdapter

Converts internal messages to OpenAI‑compatible format. Supports:

  • apiUrl – full URL (highest priority)
  • baseUrl + path – base domain + API path
  • Defaults to https://api.openai.com/v1/chat/completions

| Config field | Type | Default | Description | |-------------|------|---------|-------------| | apiKey | string | required | Bearer token for Authorization header | | apiUrl | string | – | Full request URL (overrides baseUrl+path) | | baseUrl | string | https://api.openai.com | API base domain | | path | string | /v1/chat/completions | API endpoint path |

toolCallingPlugin

Detects tool_calls in assistant responses, executes registered tools, feeds results back, and continues the conversation (up to maxIterations = 5).

  • chat.registerTool(name, description, executor, parameters?) – register a tool
  • Automatically injects tool role messages into the conversation
  • Recovers from tool execution errors gracefully (logs error, returns error message to model)

📡 Events (EventEmitter)

ChatService extends EventEmitter. Subscribe with chat.on(event, handler):

| Event | Payload | When | |-------|---------|------| | sending | { addUser, userInput, timestamp } | Before each request | | message | { role, content, ... } | Full assistant message received | | stream-progress | chunk object | Each streaming chunk arrives | | error | { error, timestamp } | Any error during request |

chat.on('sending', ({ userInput }) => console.log('Sending:', userInput));
chat.on('message', msg => console.log('Got:', msg.content));
chat.on('error', ({ error }) => console.error('Error:', error.message));

// on() returns an unsubscribe function
const unsubscribe = chat.on('message', handler);
unsubscribe(); // stop listening

🧩 ChatService API

| Method | Returns | Description | |--------|---------|-------------| | chat.send(userInput) | Promise<Message> | Send a message, get reply (non‑streaming) | | chat.stream(userInput, onProgress, onDone) | Promise<Message> | Send a message, get streaming reply | | chat.sendExisting() | Promise<Message> | Re‑send current messages without adding user input | | chat.sendExistingStream(onProgress, onDone) | Promise<Message> | Same as above, streaming | | chat.use(plugin) | this | Install a plugin/adapter | | chat.setAdapter(adapter) | void | Manually set the adapter | | chat.on(event, handler) | unsubscribe function | Subscribe to events | | chat.registerTool(name, desc, fn, params?) | this | Register a tool (requires toolCallingPlugin) | | chat.messages | MessageStore | Access the message store directly |


🗄️ MessageStore API

| Method | Description | |--------|-------------| | add(message) | Add a raw message object | | addUser(content, meta?) | Add a user message | | addAssistant(content, meta?) | Add an assistant message | | addSystem(content, meta?) | Add a system message | | addTool(content, toolCallId, meta?) | Add a tool result message | | getAll() | Return a shallow copy of all messages | | getLast() | Return the last message (or null) | | clear() | Remove all messages | | undoToLastAssistant() | Remove messages after the last assistant message |

Message format:

{
  id: string,           // auto‑generated if not provided
  role: 'user' | 'assistant' | 'system' | 'tool',
  content: string,
  toolCalls?: Array,    // assistant messages with tool calls
  toolCallId?: string,  // tool messages
  reasoningContent?: string,  // deepseek-reasoner
  timestamp?: number,
  metadata?: any
}

❌ Error Handling

The framework throws typed errors for different failure modes:

| Error Class | .name | When | |------------|---------|------| | APIError | 'APIError' | Non‑2xx HTTP responses (401, 429, 500, etc.) | | NetworkError | 'NetworkError' | fetch failures, connection timeouts | | ConfigurationError | 'ConfigurationError' | Missing required config | | ParsingError | 'ParsingError' | Malformed API response |

import { APIError, NetworkError, ConfigurationError, ParsingError } from 'my-ai-chat-framework';

try {
  await chat.send('Hello');
} catch (error) {
  if (error instanceof APIError) {
    console.error(`API ${error.statusCode}: ${error.message}`);
  } else if (error instanceof NetworkError) {
    console.error('Network issue:', error.message);
  }
}

📚 Configuration Reference

new ChatService(config) accepts:

| Option | Type | Default | Description | |--------|------|---------|-------------| | apiKey | string | required | Your API key | | baseUrl | string | 'https://api.openai.com' | API base URL (used with path) | | path | string | '/v1/chat/completions' | API path (used with baseUrl) | | apiUrl | string | – | Full API URL (overrides baseUrl+path) | | model | string | required | Model name (e.g., deepseek-chat) | | modelParams | object | {} | Grouped model parameters (see below) | | temperature | number | 0.7 | Sampling temperature (0–2) | | maxTokens | number | 2000 | Max tokens to generate | | reasoningEffort | string | – | For deepseek-reasoner: 'low', 'medium', 'high' |

Both flat and modelParams styles work. modelParams takes precedence over top‑level values.


🧪 Testing

# 1. Create a .env file with your API key
echo "DEEPSEEK_API_KEY=sk-xxxxx" > .env

# 2. Run the test
npm test

The test script (test.js) sends a simple message and logs the response.


🛠️ Development

git clone https://github.com/your-username/my-ai-chat-framework.git
cd my-ai-chat-framework
npm install

# Dev mode (watching)
npm run dev

# Build the library (ES + UMD + CJS)
npm run build

# Run tests
npm test

📄 License

MIT