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

@ubean/ai

v0.8.0

Published

AI large-model integration for the ubean framework: defineAgent + defineAgentTool + Vue runtime (thin orchestration over the Vercel AI SDK)

Readme

@ubean/ai

English | 中文

AI large-model integration for the ubean framework — a thin orchestration layer over the Vercel AI SDK.

import { defineAgent, defineAgentTool, v } from '@ubean/ai';

const weather = defineAgentTool({
  name: 'get_weather',
  description: 'Get the weather for a city',
  input: v.object({ city: v.string() }),
  execute: async ({ city }) => ({ city, temperature: 21 })
});

const agent = defineAgent({ model: 'deepseek-chat', tools: { weather } });

Table of Contents

Package Positioning

| Layer | Responsibility | | ----------------------- | ----------------------------------------------------------------------------------- | | @ubean/ai (kernel) | Provider registry, agent definition, tool definition, streaming chunk protocol | | @ubean/ai/runtime/vue | Vue composables that consume the SSE stream an agent exposes | | @ubean/ai/gateway | Provider presets only — base URLs and ids for common hosted providers | | @ubean/ai/vite | Module wiring: registers providers, auto-imports the composables, adds optimizeDeps |

Included:

  • defineProvider / configureAI / resolveModel — resolve a model string to a concrete provider + model id.
  • defineAgent — declares system prompt, model, tools, step budget and sampling knobs; returns a UbeanAgent with run(), stream() and asHandler().
  • defineAgentTool — a type-safe identity helper that keeps the Valibot schema's inferred input type on execute(args, ctx).
  • v — a namespace re-export of valibot, used to author tool input schemas.
  • useChat / useAgent / useAIProvider — the Vue runtime.
  • Provider presets for DeepSeek, OpenRouter, OmniRoute, OpenAI, Anthropic, Google and Groq.

NOT included (deliberately):

  • No gateway service. @ubean/ai/gateway ships provider presets, not routing, fallback or cost optimization — put an external gateway (for example OmniRoute) in front if you need those.
  • No UI layer. Markdown rendering, code highlighting and message bubbles are out of scope; pick your own renderer in the useChat consumer.
  • No agent loop implementation. The tool-calling loop, retries and streaming come from the Vercel AI SDK (ai); ubean only declares and orchestrates.
  • No HTTP route files. The asHandler() result is a plain Hono handler — mount it in src/routes/ yourself, the way @ubean/routes documents API routes.

Runtime dependency whitelist (main entry): @valibot/to-json-schema, valibot. The AI SDK itself (ai, @ai-sdk/openai-compatible) is loaded lazily.

Optional peers: ai, @ai-sdk/openai-compatible, hono, vite, vue. Every peer is optional — the package builds and imports without them. If the AI SDK is missing when an agent actually runs, the call rejects with an install hint:

[@ubean/ai] The AI SDK packages are not installed. Install them with:
pnpm add ai @ai-sdk/openai-compatible (or npm/yarn/bun equivalent).

Install

# the ubean-side orchestration layer
pnpm add @ubean/ai

# the AI SDK runtime — required at agent execution time
pnpm add ai @ai-sdk/openai-compatible

# peers you only need if you use that part of the surface
pnpm add vue        # @ubean/ai/runtime/vue
pnpm add hono       # UbeanAgent.asHandler()

Quick Start

1. Enable the module

// ubean.config.ts
import { defineConfig } from 'ubean';

export default defineConfig({
  ai: true // or ai: { defaultProvider: 'deepseek', defaultModel: 'deepseek-chat' }
});

2. Define a tool

Tool input is a Valibot schema, so execute receives a fully inferred argument object.

// src/server/ai/tools.ts
import { defineAgentTool, v } from '@ubean/ai';

export const searchDocs = defineAgentTool({
  name: 'search_docs',
  description: 'Search the documentation index for a query',
  input: v.object({
    query: v.string(),
    limit: v.optional(v.pipe(v.number(), v.integer()), 5)
  }),
  execute: async ({ query, limit }, ctx) => {
    ctx.signal?.throwIfAborted();
    return { query, limit, hits: [] };
  }
});

3. Define an agent

// src/server/ai/agent.ts
import { defineAgent } from '@ubean/ai';
import { searchDocs } from './tools';

export const supportAgent = defineAgent<{ answer: string }>({
  model: 'deepseek-chat',
  system: 'You are a documentation assistant. Cite the docs you used.',
  tools: { searchDocs },
  maxSteps: 5
});

4. Expose it over HTTP

// src/routes/api/chat.ts
import { supportAgent } from '~/server/ai/agent';

export const POST = supportAgent.asHandler();

asHandler() reads the request body as an AgentInput and responds with a Server-Sent Events stream of AgentStreamChunk frames.

Agent Kernel

defineAgent(config) accepts a model string, a system prompt, a tool set, and optional sampling knobs (temperature, maxTokens, headers, provider, context). It returns a UbeanAgent:

| Member | Description | | --------------- | ---------------------------------------------------------------------------------------- | | config | The resolved agent configuration. | | run(input) | Await the full result: { text, output?, toolResults, usage? }. | | stream(input) | AsyncIterable<AgentStreamChunk> — text deltas, tool calls, tool results, errors, done. | | asHandler() | A Hono Handler that streams the agent over SSE. |

Providers are resolved by model string. Register one with defineProvider, or set a default through configureAI:

import { configureAI, defineProvider, resolveModel } from '@ubean/ai';

defineProvider({
  id: 'my-proxy',
  kind: 'openai-compatible',
  baseURL: 'https://gateway.internal/v1',
  apiKey: process.env.MY_PROXY_KEY,
  modelAliases: { chat: 'deepseek-chat' }
});

configureAI({ defaultProvider: 'my-proxy', defaultModel: 'chat' });

const { provider, modelId } = resolveModel('chat');

defineProvider is idempotent per id and ignores ids containing / (those are treated as explicit provider/model strings). When no provider matches, resolveModel throws:

[@ubean/ai] No provider resolved for "<model>". Define a provider via defineProvider() or configure a default with configureAI().

If nothing is configured, an environment fallback provider (id: 'env', kind: 'openai-compatible') is used, taking its key from DEEPSEEK_API_KEY || UBEAN_AI_API_KEY || OPENAI_API_KEY and its base URL from UBEAN_AI_API_BASE (default https://api.deepseek.com/v1).

Vue Runtime

@ubean/ai/runtime/vue is a self-built, SSR-safe SSE consumer. It does not depend on @ai-sdk/vue.

import { useChat } from '@ubean/ai/runtime/vue';

const { messages, input, setInput, sendMessage, status, error, isStreaming, abort, reload } = useChat({
  url: '/api/chat'
});

| Composable | Returns | | ------------------ | --------------------------------------------------------------------------------------------- | | useChat(options) | { messages, status, error, input, data, setInput, sendMessage, abort, reload, isStreaming } | | useAgent(agent) | { isStreaming, error, result, chunks, run, stream } — drives an in-process UbeanAgent | | useAIProvider() | { defaultProvider?, defaultModel? } — the runtime provider configuration |

useChat defaults its url to the relative ./chat, POSTs { ...body, messages }, and parses AgentStreamChunk frames into ChatMessage[]. Set the runtime provider defaults with setAIProviderConfig({ defaultProvider, defaultModel }).

Gateway Presets

@ubean/ai/gateway exports provider definitions you can register directly:

import { defineProvider } from '@ubean/ai';
import { deepseek, openrouter, allGatewayProviders } from '@ubean/ai/gateway';

for (const provider of allGatewayProviders) defineProvider(provider);

| Export | Provider id | Base URL | | ------------ | ------------ | ------------------------------ | | deepseek | deepseek | https://api.deepseek.com/v1 | | openrouter | openrouter | https://openrouter.ai/api/v1 | | omniRoute | omniroute | http://localhost:8080/v1 | | openai | openai | https://api.openai.com/v1 | | anthropic | anthropic | — | | google | google | — | | groq | groq | — |

This subpath provides presets only. It does not implement gateway-level routing, fallback or cost optimization — that belongs to an external gateway such as OmniRoute.

Vite Plugin

The ai field in ubean.config.ts wires the module for you. When you need explicit control, use the plugin directly:

import { ubeanAiPlugin, defineAiConfig } from '@ubean/ai/vite';

export default defineConfig({
  plugins: [ubeanAiPlugin(defineAiConfig({ defaultProvider: 'deepseek' }))]
});

The plugin registers the provider presets via defineProvider, registers a component resolver for useChat / useAgent / useAIProvider from @ubean/ai/runtime/vue, generates virtual:ubean-ai/runtime, and adds the runtime packages to optimizeDeps.include. It does not start a dev gateway proxy.

Type Safety

  • AgentTool<TInput> carries the Valibot schema, so execute(args, ctx) is inferred from input — no casts, no manual generics.
  • defineAgentTool is an identity function whose only job is to preserve that inference.
  • defineAgent<T>(config) types AgentRunResult<T>.output and AgentRunResult<T>.text.
  • AgentStreamChunk is a discriminated union on type ('text-delta' | 'tool-call' | 'tool-result' | 'error' | 'done'), so exhaustive switches work in the stream() consumer.
  • UbeanAgent.asHandler() returns a Hono Handler, which keeps route typing intact when mounted from src/routes/.

Architecture Notes

  • Thin over the AI SDK. ubean owns declaration and orchestration; the agent loop, tool-calling protocol and streaming primitives come from ai.
  • Lazy AI SDK import. Missing peers never break the module graph — they fail at execution with an actionable message.
  • v is valibot. The namespace re-export exists so tool schemas and the agent config can come from one import.
  • Streaming protocol is the contract. AgentStreamChunk is what both stream() and the Vue runtime speak, which is why useChat can consume any agent's SSE endpoint.
  • Rendering is out of scope. The runtime returns structured messages and chunks; bring your own markdown/code renderer.
  • No gateway implementation. Presets are data; routing policy stays outside the framework.