llm-adapter-client
v0.1.0
Published
TypeScript client library for llm-adapter HTTP API
Maintainers
Readme
LLM Adapter Client for TypeScript
TypeScript клиентская библиотека для взаимодействия с LLM Adapter HTTP API.
Назначение
llm-adapter-client — это типобезопасный клиент для взаимодействия с сервисом llm-adapter, предоставляющий удобный программный интерфейс для отправки запросов к LLM (Large Language Models) через HTTP API.
Основные возможности:
- ✅ Полная типизация TypeScript
- ✅ Поддержка всех эндпоинтов llm-adapter API
- ✅ Единообразная обработка ошибок
- ✅ Конфигурируемые значения по умолчанию
- ✅ Подмена HTTP-транспорта (fetch по умолчанию)
- ✅ Работа в браузере и Node.js (18+)
- ✅ Минимум зависимостей
Установка
npm install llm-adapter-clientили с yarn:
yarn add llm-adapter-clientБыстрый старт
import { createClient } from 'llm-adapter-client';
// Создание клиента
const client = createClient({
baseUrl: 'http://localhost:8080/api/v1',
defaults: {
model: 'gemini-2.5-flash',
temperature: 0.7,
max_tokens: 2048,
},
});
// Отправка запроса
const response = await client.query({
prompt: 'Напиши короткое стихотворение про TypeScript',
model: 'gemini-2.5-flash',
});
console.log(response.text);Конфигурация
ClientConfig
Параметры конфигурации клиента:
interface ClientConfig {
/** Базовый URL для llm-adapter (включая /api/v1) */
baseUrl: string;
/** Значения по умолчанию для LLM параметров */
defaults?: {
model?: string;
temperature?: number;
max_tokens?: number;
};
/** Общие HTTP-заголовки для всех запросов */
headers?: Record<string, string>;
/** Таймаут запросов в миллисекундах */
timeout?: number;
/** Кастомная реализация HTTP-транспорта */
transport?: HttpTransport;
}Примеры конфигурации
Минимальная конфигурация:
const client = createClient({
baseUrl: 'http://localhost:8080/api/v1',
});Полная конфигурация:
const client = createClient({
baseUrl: 'http://localhost:8080/api/v1',
defaults: {
model: 'gemini-2.5-flash',
temperature: 0.7,
max_tokens: 2048,
},
headers: {
'X-API-Key': process.env.API_KEY,
'X-Request-ID': generateRequestId(),
},
timeout: 30000, // 30 секунд
});API методы
chat()
Отправить сообщение в чат с историей диалога.
const response = await client.chat({
message: 'Как дела?',
history: [
{ role: 'user', content: 'Привет!' },
{ role: 'assistant', content: 'Здравствуй! Чем могу помочь?' },
],
systemInstruction: 'Ты полезный ассистент',
});
console.log(response.text);prompt()
Использовать зарегистрированный промпт с подстановкой параметров.
const response = await client.prompt({
name: 'task_classification',
parameters: {
text: 'Пользователь хочет узнать погоду',
},
model: 'gemini-2.5-flash', // опционально
temperature: 0.3, // опционально
});
console.log(response.text);promptStructured()
Использовать промпт со структурированным выводом (JSON Schema).
С явной схемой в запросе:
const response = await client.promptStructured({
name: 'extract_entities',
parameters: {
text: 'Асыл купил iPhone 15 в Алматы',
},
structured_output: {
schema: {
type: 'object',
properties: {
person: { type: 'string' },
product: { type: 'string' },
location: { type: 'string' },
},
required: ['person', 'product', 'location'],
},
},
});
console.log(response.json);
// { person: 'Асыл', product: 'iPhone 15', location: 'Алматы' }Со схемой из YAML frontmatter промпта (structured_output опционален):
Если промпт содержит structured_output в YAML frontmatter, параметр structured_output в запросе можно не передавать:
// Промпт classify_task.md содержит structured_output в frontmatter
const response = await client.promptStructured({
name: 'classify_task',
parameters: {
user_message: 'Привет!',
dialog_history: '',
},
// structured_output не передаётся — используется из промпта
});
console.log(response.json);
// { task_type: 'CHIT_CHAT', goal: 'Приветствие', reasoning: '...' }query()
Прямой запрос к LLM с текстовым промптом.
const response = await client.query({
prompt: 'Объясни что такое замыкания в JavaScript',
model: 'gemini-2.5-flash',
temperature: 0.7,
max_tokens: 1024,
});
console.log(response.text);queryStructured()
Прямой запрос к LLM со структурированным выводом.
const response = await client.queryStructured({
prompt: 'Извлеки из текста имя, возраст и город: "Меня зовут Мариана, мне 25 лет, живу в Алматы"',
model: 'gemini-2.5-flash',
structured_output: {
schema: {
type: 'object',
properties: {
name: { type: 'string' },
age: { type: 'number' },
city: { type: 'string' },
},
required: ['name', 'age', 'city'],
},
},
});
console.log(response.json);
// { name: 'Мариана', age: 25, city: 'Алматы' }getPrompts()
Получить список доступных промптов.
const response = await client.getPrompts();
response.prompts?.forEach((prompt) => {
console.log(`${prompt.name}: [${prompt.parameters?.join(', ')}]`);
});Обработка ошибок
Клиент использует систему типизированных исключений для обработки ошибок.
Типы ошибок
NetworkError— сетевые ошибки (нет соединения, таймаут)ValidationError— ошибки валидации (HTTP 400)NotFoundError— ресурс не найден (HTTP 404, промпт не существует)LLMError— бизнес-ошибки LLM (когдаresponse.errorзаполнен)ServerError— ошибки сервера (HTTP 5xx)LLMAdapterError— базовый класс для всех ошибок
Пример обработки
import {
ValidationError,
NotFoundError,
NetworkError,
LLMError,
} from 'llm-adapter-client';
try {
const response = await client.prompt({
name: 'my_prompt',
parameters: { text: 'Hello' },
});
console.log(response.text);
} catch (error) {
if (error instanceof NotFoundError) {
console.error('Промпт не найден:', error.message);
} else if (error instanceof ValidationError) {
console.error('Ошибка валидации:', error.message);
} else if (error instanceof NetworkError) {
console.error('Сетевая ошибка:', error.message);
} else if (error instanceof LLMError) {
console.error('Ошибка LLM:', error.message);
} else {
console.error('Неизвестная ошибка:', error);
}
}Доступ к деталям ошибки
Все ошибки содержат дополнительную информацию:
try {
await client.query({ prompt: 'test', model: 'unknown' });
} catch (error) {
if (error instanceof LLMAdapterError) {
console.log('HTTP статус:', error.status);
console.log('Оригинальный ответ:', error.originalResponse);
console.log('Сообщение:', error.message);
}
}Кастомный HTTP-транспорт
Вы можете подменить реализацию HTTP-клиента, передав свой транспорт:
import { HttpTransport, HttpTransportOptions, HttpTransportResponse } from 'llm-adapter-client';
import axios from 'axios';
class AxiosTransport implements HttpTransport {
constructor(private baseUrl: string) {}
async request<T>(options: HttpTransportOptions): Promise<HttpTransportResponse<T>> {
const response = await axios({
method: options.method,
url: `${this.baseUrl}${options.path}`,
headers: options.headers,
data: options.body,
timeout: options.timeout,
});
return {
status: response.status,
data: response.data as T,
headers: response.headers as Record<string, string>,
};
}
}
const client = createClient({
baseUrl: 'http://localhost:8080/api/v1',
transport: new AxiosTransport('http://localhost:8080/api/v1'),
});Использование defaults
Параметры model, temperature и max_tokens можно задать на уровне клиента и переопределить при вызове:
const client = createClient({
baseUrl: 'http://localhost:8080/api/v1',
defaults: {
model: 'gemini-2.5-flash',
temperature: 0.7,
max_tokens: 2048,
},
});
// Использует defaults
await client.query({ prompt: 'Hello' });
// Переопределяет температуру
await client.query({
prompt: 'Hello',
temperature: 1.0, // переопределено
});
// Переопределяет модель и max_tokens
await client.prompt({
name: 'my_prompt',
model: 'gemini-1.5-pro', // переопределено
max_tokens: 4096, // переопределено
});Примеры использования
Чат-бот с историей
const history: ChatMessage[] = [];
async function sendMessage(userMessage: string) {
const response = await client.chat({
message: userMessage,
history,
systemInstruction: 'Ты дружелюбный помощник',
});
// Обновляем историю
history.push({ role: 'user', content: userMessage });
history.push({ role: 'assistant', content: response.text || '' });
return response.text;
}
await sendMessage('Привет!');
await sendMessage('Как дела?');Классификация задач
async function classifyTask(userText: string) {
const response = await client.promptStructured({
name: 'task_classification',
parameters: { text: userText },
structured_output: {
schema: {
type: 'object',
properties: {
category: {
type: 'string',
enum: ['QUESTION', 'COMMAND', 'CHIT_CHAT', 'OTHER'],
},
confidence: { type: 'number' },
},
required: ['category', 'confidence'],
},
},
});
return response.json as { category: string; confidence: number };
}
const result = await classifyTask('Какая сегодня погода в Алматы?');
console.log(result); // { category: 'QUESTION', confidence: 0.95 }Извлечение структурированных данных
async function extractContactInfo(text: string) {
const response = await client.queryStructured({
prompt: `Извлеки контактную информацию из текста: "${text}"`,
model: 'gemini-2.5-flash',
structured_output: {
schema: {
type: 'object',
properties: {
name: { type: 'string' },
email: { type: 'string' },
phone: { type: 'string' },
},
},
},
});
return response.json;
}
const contacts = await extractContactInfo(
'Свяжитесь с Асылом Шабаевым: [email protected], +7 777 123-45-67'
);
// Другой пример с извлечением данных
const personInfo = await client.queryStructured({
prompt: 'Извлеки информацию о человеке: "Алексей Другой работает в Талдыкоргане, ему 40 лет"',
model: 'gemini-2.5-flash',
structured_output: {
schema: {
type: 'object',
properties: {
name: { type: 'string' },
surname: { type: 'string' },
age: { type: 'number' },
city: { type: 'string' },
},
required: ['name', 'surname', 'age', 'city'],
},
},
});
console.log(personInfo.json);
// { name: 'Алексей', surname: 'Другой', age: 30, city: 'Талдыкорган' }Swagger документация
Полная спецификация API доступна в Swagger документации проекта LLM Adapter.
Требования
- Node.js >= 18.0.0 (для нативного
fetch) - TypeScript >= 5.0 (опционально, для разработки)
Лицензия
MIT
