@omelhorsite/sdk 0.5.0 → 0.11.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.
@@ -0,0 +1,296 @@
1
+ /**
2
+ * The `llm` namespace: the language models a signed-in person may pick, and
3
+ * what they have used.
4
+ *
5
+ * Which models exist, what they cost and who may use how much of them is
6
+ * administered on the server; this surface only reads. The administrator
7
+ * side lives under `oms.admin.llmProviders`, `oms.admin.llmModels`,
8
+ * `oms.admin.llmAssignments` and `oms.admin.llmUsage`.
9
+ */
10
+ import { Resource, type ApiClient } from "../http";
11
+ import type { ListParams } from "../listing";
12
+ import type { Id, Paginated, RequestOptions, Timestamp } from "../types";
13
+ /** What a model can do. Absent keys mean "unknown", not "no". */
14
+ export declare const LLM_CAPABILITY_KEYS: readonly ["vision", "audio", "tools", "json", "reasoning", "streaming"];
15
+ export type LlmCapabilityKey = (typeof LLM_CAPABILITY_KEYS)[number];
16
+ export type LlmCapabilities = {
17
+ readonly [K in LlmCapabilityKey]?: boolean;
18
+ };
19
+ /**
20
+ * Per-model ceilings. `rpm` is informational; the two `*_per_day_per_user`
21
+ * keys are enforced per calendar day (server time) and answer `429` when hit.
22
+ * A missing key or `0` means no ceiling.
23
+ */
24
+ export declare const LLM_LIMIT_KEYS: readonly ["rpm", "requests_per_day_per_user", "tokens_per_day_per_user"];
25
+ export type LlmLimitKey = (typeof LLM_LIMIT_KEYS)[number];
26
+ export type LlmLimits = {
27
+ readonly [K in LlmLimitKey]?: number;
28
+ };
29
+ /** How much of a model's daily ceilings the caller has used today. `null` remaining means no ceiling. */
30
+ export interface LlmModelUsageToday {
31
+ readonly requests_today: number;
32
+ readonly tokens_today: number;
33
+ readonly requests_remaining: number | null;
34
+ readonly tokens_remaining: number | null;
35
+ }
36
+ /** One model the caller may choose. Prices are per million tokens, `null` when unknown. */
37
+ export interface LlmModelChoice {
38
+ readonly id: Id;
39
+ /** The provider's own identifier, e.g. `"qwen/qwen3.7-flash"`. */
40
+ readonly model_id: string;
41
+ readonly name: string;
42
+ readonly provider_slug: string;
43
+ readonly free: boolean;
44
+ readonly context_window: number | null;
45
+ readonly max_output_tokens: number | null;
46
+ readonly input_price_per_million: number | null;
47
+ readonly output_price_per_million: number | null;
48
+ readonly capabilities: LlmCapabilities;
49
+ readonly limits: LlmLimits;
50
+ readonly usage: LlmModelUsageToday;
51
+ }
52
+ /** Cost is in the provider's currency (USD for the hosted ones), already summed. */
53
+ export interface LlmUsageTotals {
54
+ readonly requests: number;
55
+ readonly tokens: number;
56
+ readonly cost: number;
57
+ readonly errors: number;
58
+ }
59
+ /** One day of a series. `date` is `YYYY-MM-DD`; days without calls carry zeros. */
60
+ export interface LlmUsageDay {
61
+ readonly date: string;
62
+ readonly requests: number;
63
+ readonly tokens: number;
64
+ readonly cost: number;
65
+ }
66
+ export interface LlmUsageByFeature {
67
+ readonly feature: string;
68
+ readonly requests: number;
69
+ readonly tokens: number;
70
+ readonly cost: number;
71
+ }
72
+ export interface LlmUsageByModel {
73
+ /** `null` when the provider has since been deleted. */
74
+ readonly provider_slug: string | null;
75
+ readonly model_id: string;
76
+ readonly requests: number;
77
+ readonly tokens: number;
78
+ readonly cost: number;
79
+ }
80
+ /** One person's share, on the administrator summary. `null` fields belong to calls made by background work. */
81
+ export interface LlmUsageByUser {
82
+ readonly user_id: Id | null;
83
+ readonly handle: string | null;
84
+ readonly name: string | null;
85
+ readonly requests: number;
86
+ readonly tokens: number;
87
+ readonly cost: number;
88
+ }
89
+ /**
90
+ * Usage over a window of days. `daily` has exactly `days` entries, oldest
91
+ * first, ending today. The breakdowns count successful calls only; `errors`
92
+ * in the totals counts the failed ones.
93
+ */
94
+ export interface LlmUsageSummary {
95
+ readonly days: number;
96
+ readonly totals: {
97
+ readonly today: LlmUsageTotals;
98
+ readonly last_7d: LlmUsageTotals;
99
+ readonly last_30d: LlmUsageTotals;
100
+ /** The requested window. */
101
+ readonly window: LlmUsageTotals;
102
+ };
103
+ readonly daily: LlmUsageDay[];
104
+ readonly by_feature: LlmUsageByFeature[];
105
+ readonly by_model: LlmUsageByModel[];
106
+ }
107
+ /** Windows longer than this answer `400`. */
108
+ export declare const LLM_USAGE_MAX_DAYS = 90;
109
+ export interface LlmUsageQuery {
110
+ /** 1 to {@link LLM_USAGE_MAX_DAYS}; defaults to 30. */
111
+ readonly days?: number;
112
+ }
113
+ /** Filter columns of `GET /llm_chats`, on top of the base ones. */
114
+ export declare const LLM_CHAT_FILTER_COLUMNS: readonly ["title", "pinned", "archived", "llm_model_id", "tools_enabled", "account_tools_enabled"];
115
+ export interface ListLlmChatsParams extends ListParams<(typeof LLM_CHAT_FILTER_COLUMNS)[number]> {
116
+ }
117
+ export declare const LLM_CHAT_ROLES: readonly ["user", "assistant", "system", "tool"];
118
+ export type LlmChatRole = (typeof LLM_CHAT_ROLES)[number];
119
+ /** `streaming` while the answer is still being written, then `done` or `error`. */
120
+ export declare const LLM_CHAT_MESSAGE_STATUSES: readonly ["streaming", "done", "error"];
121
+ export type LlmChatMessageStatus = (typeof LLM_CHAT_MESSAGE_STATUSES)[number];
122
+ /**
123
+ * The tools the assistant can use in a chat. `web_search` and `read_url` are
124
+ * the web tools (`tools_enabled`); `search` and `execute` are the account
125
+ * tools (`account_tools_enabled`): the assistant looks up the SDK's type
126
+ * declarations and runs TypeScript against the API as the user.
127
+ */
128
+ export declare const LLM_TOOL_NAMES: readonly ["web_search", "read_url", "search", "execute"];
129
+ export type LlmToolName = (typeof LLM_TOOL_NAMES)[number] | (string & {});
130
+ /** `running` only ever appears in the stream; a stored call is `done` or `error`. */
131
+ export declare const LLM_TOOL_CALL_STATUSES: readonly ["running", "done", "error"];
132
+ export type LlmToolCallStatus = (typeof LLM_TOOL_CALL_STATUSES)[number];
133
+ /**
134
+ * One use of a tool: what was asked (`args`), a one-line `summary` for people, how long it took.
135
+ * `args` is the tool's input: `{ query }` for `web_search` and `search`, `{ url }` for `read_url`,
136
+ * and for `execute` `{ code, code_chars }` - the head of the program (2,000 characters at most)
137
+ * and the full length it had.
138
+ */
139
+ export interface LlmToolCall {
140
+ readonly name: LlmToolName;
141
+ readonly args: Readonly<Record<string, unknown>>;
142
+ readonly status: LlmToolCallStatus;
143
+ readonly summary?: string;
144
+ readonly ms?: number;
145
+ readonly error?: string;
146
+ }
147
+ /** One conversation with the assistant. Pinned chats list first, then by `last_message_at`. */
148
+ export interface LlmChat {
149
+ readonly id: Id;
150
+ readonly created_at: Timestamp;
151
+ readonly updated_at: Timestamp;
152
+ /** Taken from the first question when not set by the caller. */
153
+ readonly title: string | null;
154
+ /** The chosen model; `null` means the server's default for chats. */
155
+ readonly llm_model_id: Id | null;
156
+ /** The provider's identifier of the model that answered last. */
157
+ readonly model_id: string | null;
158
+ readonly pinned: boolean;
159
+ /** An archived chat can be read but not written to. */
160
+ readonly archived: boolean;
161
+ /** Whether the assistant may search the web and read pages in this chat. On by default. */
162
+ readonly tools_enabled: boolean;
163
+ /**
164
+ * Whether the assistant may act on the user's account in this chat (the
165
+ * `search` and `execute` tools: it reads the SDK's declarations and runs
166
+ * TypeScript against the API as the user, with a short-lived token that
167
+ * dies with each answer). Off by default; every write it wants to make
168
+ * it is told to confirm first.
169
+ */
170
+ readonly account_tools_enabled: boolean;
171
+ readonly last_message_at: Timestamp;
172
+ readonly message_count: number;
173
+ }
174
+ export interface LlmChatMessage {
175
+ readonly id: Id;
176
+ readonly created_at: Timestamp;
177
+ readonly updated_at: Timestamp;
178
+ readonly llm_chat_id: Id;
179
+ readonly role: LlmChatRole;
180
+ readonly content: string;
181
+ readonly status: LlmChatMessageStatus;
182
+ readonly model_id: string | null;
183
+ readonly input_tokens: number | null;
184
+ readonly output_tokens: number | null;
185
+ readonly cost: number | null;
186
+ /** Why an answer ended in `error`, or `"interrupted"` on a `done` answer cut short by the reader. */
187
+ readonly error: string | null;
188
+ /** What the assistant did with its tools while writing this answer, in order. */
189
+ readonly tool_calls: readonly LlmToolCall[];
190
+ /** From the request to the model until the answer ended, in milliseconds; `null` on questions. */
191
+ readonly duration_ms: number | null;
192
+ /** From the request until the first token; `null` when nothing was generated. Tokens per second is `output_tokens / ((duration_ms - first_token_ms) / 1000)`. */
193
+ readonly first_token_ms: number | null;
194
+ /** The question this answer belongs to. */
195
+ readonly parent_id: Id | null;
196
+ }
197
+ /** `GET /llm_chats/:id`: the chat plus its messages, oldest first. */
198
+ export interface LlmChatDetail extends LlmChat {
199
+ readonly messages: LlmChatMessage[];
200
+ }
201
+ export interface CreateLlmChatInput {
202
+ readonly title?: string;
203
+ /** Must be a model the caller may choose (see {@link LlmNamespace.models}); the server answers `400` otherwise. */
204
+ readonly llmModelId?: Id;
205
+ /** Defaults to `true`. */
206
+ readonly toolsEnabled?: boolean;
207
+ /** Defaults to `false`. See {@link LlmChat.account_tools_enabled}. */
208
+ readonly accountToolsEnabled?: boolean;
209
+ }
210
+ export interface UpdateLlmChatInput {
211
+ readonly title?: string | null;
212
+ readonly llmModelId?: Id | null;
213
+ readonly pinned?: boolean;
214
+ readonly archived?: boolean;
215
+ readonly toolsEnabled?: boolean;
216
+ readonly accountToolsEnabled?: boolean;
217
+ }
218
+ export interface SendLlmChatMessageInput {
219
+ /** At most 32,000 characters. */
220
+ readonly content: string;
221
+ /** Answer with this model and remember it on the chat. */
222
+ readonly llmModelId?: Id;
223
+ }
224
+ /**
225
+ * What a streamed answer yields, in order: any number of `delta` and `tool`
226
+ * events (a tool yields `running` first, then `done` or `error`), then
227
+ * exactly one `done` or `error`. A refusal before the first token (the
228
+ * provider is busy, the daily ceiling is reached, no model is available) is
229
+ * not an event but a thrown `OmsApiError` with status `503`, `429` or `502`,
230
+ * whose body carries `message_id` - the failed answer is kept on the chat so
231
+ * it can be regenerated.
232
+ */
233
+ export type LlmChatStreamEvent = {
234
+ readonly type: "delta";
235
+ readonly delta: string;
236
+ } | ({
237
+ readonly type: "tool";
238
+ } & LlmToolCall) | {
239
+ readonly type: "done";
240
+ readonly messageId: Id;
241
+ readonly modelId: string | null;
242
+ readonly inputTokens: number | null;
243
+ readonly outputTokens: number | null;
244
+ readonly cost: number | null;
245
+ readonly durationMs: number | null;
246
+ readonly firstTokenMs: number | null;
247
+ } | {
248
+ readonly type: "error";
249
+ /** `busy`, `limit`, `unavailable`, `unknown_model` or `interrupted`. */
250
+ readonly error: string;
251
+ readonly message: string;
252
+ readonly messageId: Id | null;
253
+ };
254
+ /** The messages of one chat: send, regenerate, delete. Reached through `oms.llm.chats.messages`. */
255
+ export declare class LlmChatMessagesNamespace extends Resource {
256
+ /**
257
+ * Sends a question and streams the answer. Consume with `for await`; stop
258
+ * early by aborting `options.signal` - the server keeps what had arrived.
259
+ * The chat's title is set from the first question.
260
+ */
261
+ send(chatId: Id, input: SendLlmChatMessageInput, options?: RequestOptions): AsyncGenerator<LlmChatStreamEvent, void, undefined>;
262
+ /**
263
+ * Answers the same question again. Only the chat's last message qualifies,
264
+ * and it must be an answer; the old answer is discarded, the new one takes
265
+ * its place with the same `parent_id`.
266
+ */
267
+ regenerate(chatId: Id, messageId: Id, options?: RequestOptions): AsyncGenerator<LlmChatStreamEvent, void, undefined>;
268
+ /**
269
+ * Removes a message from the end of the chat. Deleting a question also
270
+ * removes the answers to it; anything earlier answers `400`.
271
+ */
272
+ delete(chatId: Id, messageId: Id, options?: RequestOptions): Promise<void>;
273
+ private stream;
274
+ }
275
+ /** The caller's conversations with the assistant. Reached through `oms.llm.chats`. */
276
+ export declare class LlmChatsNamespace extends Resource {
277
+ readonly messages: LlmChatMessagesNamespace;
278
+ constructor(http: ApiClient);
279
+ /** Pinned first, then most recently active. Archived chats are included; filter with `exactSearch: { archived: false }`. */
280
+ list(params?: ListLlmChatsParams, options?: RequestOptions): Promise<Paginated<LlmChat>>;
281
+ /** The chat with all its messages, oldest first. */
282
+ get(id: Id, options?: RequestOptions): Promise<LlmChatDetail>;
283
+ create(input?: CreateLlmChatInput, options?: RequestOptions): Promise<LlmChatDetail>;
284
+ update(id: Id, input: UpdateLlmChatInput, options?: RequestOptions): Promise<LlmChatDetail>;
285
+ /** Removes the chat and every message in it. */
286
+ delete(id: Id, options?: RequestOptions): Promise<void>;
287
+ }
288
+ export declare class LlmNamespace extends Resource {
289
+ /** Conversations with the assistant. */
290
+ readonly chats: LlmChatsNamespace;
291
+ constructor(http: ApiClient);
292
+ /** The models the caller may choose, with today's remaining allowance on each. */
293
+ models(options?: RequestOptions): Promise<LlmModelChoice[]>;
294
+ /** The caller's own usage. */
295
+ usage(input?: LlmUsageQuery, options?: RequestOptions): Promise<LlmUsageSummary>;
296
+ }
@@ -0,0 +1,116 @@
1
+ /**
2
+ * The `search` namespace: web search through the site's own metasearch engine.
3
+ *
4
+ * One request, one answer: results from several upstream engines merged and
5
+ * scored server-side, plus the query suggestions and knowledge-panel style
6
+ * infoboxes the engines offered. Nothing is paged in the background - a page
7
+ * is what you asked for, and {@link SearchResponse.has_more} says whether it
8
+ * is worth asking for the next one.
9
+ *
10
+ * Requires a credential. The server answers the same query from a short-lived
11
+ * cache, so repeating a search (or paging back) is cheap.
12
+ */
13
+ import { Resource } from "../http";
14
+ import type { RequestOptions } from "../types";
15
+ /** The four result families. Each one changes what a {@link SearchResult} carries. */
16
+ export declare const SEARCH_CATEGORIES: readonly ["general", "images", "news", "videos"];
17
+ export type SearchCategory = (typeof SEARCH_CATEGORIES)[number];
18
+ /** Restricts results to things published inside the window. */
19
+ export declare const SEARCH_TIME_RANGES: readonly ["day", "week", "month", "year"];
20
+ export type SearchTimeRange = (typeof SEARCH_TIME_RANGES)[number];
21
+ /** `0` off, `1` moderate (the default), `2` strict. */
22
+ export type SearchSafeSearch = 0 | 1 | 2;
23
+ /** Pages beyond this answer `400`. */
24
+ export declare const SEARCH_MAX_PAGE = 10;
25
+ /** Longer queries answer `400`. */
26
+ export declare const SEARCH_MAX_QUERY_LENGTH = 200;
27
+ export interface SearchQueryInput {
28
+ /** What to search for. Trimmed server-side; blank is a `400`. */
29
+ readonly q: string;
30
+ /** 1-based, at most {@link SEARCH_MAX_PAGE}. Defaults to 1. */
31
+ readonly page?: number;
32
+ /** Defaults to `"general"`. */
33
+ readonly category?: SearchCategory;
34
+ readonly timeRange?: SearchTimeRange;
35
+ /**
36
+ * A language code such as `"pt"`, `"pt-PT"` or `"en"`, or `"all"` to search
37
+ * without a language filter. When omitted the server reads the request's
38
+ * `Accept-Language`, and falls back to Portuguese.
39
+ */
40
+ readonly language?: string;
41
+ /** Defaults to `1`. */
42
+ readonly safesearch?: SearchSafeSearch;
43
+ }
44
+ /**
45
+ * One hit.
46
+ *
47
+ * `image`, `resolution` and `duration` are filled only by the category that
48
+ * has them (images for the first two, videos for the last) and are `null`
49
+ * otherwise; `thumbnail` may appear in any category. `published_at` is the
50
+ * upstream engine's own timestamp string and is not normalised to one format.
51
+ */
52
+ export interface SearchResult {
53
+ /** Never empty: a hit without a title carries its host instead. */
54
+ readonly title: string;
55
+ /** Always absolute `http(s)`. */
56
+ readonly url: string;
57
+ /** The hostname of `url` without a leading `www.`. */
58
+ readonly host: string;
59
+ /** Plain text, at most 600 characters. Empty when the engine gave none. */
60
+ readonly snippet: string;
61
+ /** Upstream engines that returned this hit, deduplicated. */
62
+ readonly engines: string[];
63
+ readonly thumbnail: string | null;
64
+ /** The full-size image, images only. */
65
+ readonly image: string | null;
66
+ /** `"1200x800"` style, images only. */
67
+ readonly resolution: string | null;
68
+ /** As the engine spells it (`"12:34"`), videos only. */
69
+ readonly duration: string | null;
70
+ readonly published_at: string | null;
71
+ readonly category: SearchCategory | string;
72
+ }
73
+ export interface SearchInfoboxLink {
74
+ readonly title: string;
75
+ readonly url: string;
76
+ }
77
+ /** A knowledge-panel style card an engine attached to the query. */
78
+ export interface SearchInfobox {
79
+ readonly title: string;
80
+ /** Plain text, at most 1200 characters. */
81
+ readonly content: string;
82
+ readonly image: string | null;
83
+ /** At most six. */
84
+ readonly urls: SearchInfoboxLink[];
85
+ }
86
+ export interface SearchResponse {
87
+ /** The query as the engine understood it. */
88
+ readonly query: string;
89
+ readonly category: SearchCategory | string;
90
+ readonly page: number;
91
+ /** At most 40, in the server's ranking order. */
92
+ readonly results: SearchResult[];
93
+ /** At most eight related queries. */
94
+ readonly suggestions: string[];
95
+ readonly infoboxes: SearchInfobox[];
96
+ /**
97
+ * The engines' own estimate of the total. Often `0`: many engines do not
98
+ * report one, and it is never the length of anything you can page through.
99
+ */
100
+ readonly number_of_results: number;
101
+ /** Engines that failed to answer in time. The results are complete without them. */
102
+ readonly unresponsive_engines: string[];
103
+ /** Whether asking for `page + 1` is likely to return anything. */
104
+ readonly has_more: boolean;
105
+ }
106
+ /** The `search` namespace, reachable as `oms.search`. */
107
+ export declare class SearchNamespace extends Resource {
108
+ /**
109
+ * `GET /search` - runs one search and returns one page of merged results.
110
+ *
111
+ * @throws {OmsApiError} 400 when the query is blank or too long, or a
112
+ * parameter is out of range; 401 without a credential; 429 above 60
113
+ * searches a minute; 502 while the engine behind the service is down.
114
+ */
115
+ query(input: SearchQueryInput, options?: RequestOptions): Promise<SearchResponse>;
116
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@omelhorsite/sdk",
3
- "version": "0.5.0",
3
+ "version": "0.11.0",
4
4
  "description": "TypeScript SDK for the omelhorsite API. Isolate-safe: no node builtins, no environment access, no stdout.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -20,7 +20,7 @@
20
20
  "typecheck": "tsc --noEmit -p . && tsc --noEmit -p tsconfig.test.json",
21
21
  "check:isolate": "bun run scripts/check-isolate.ts",
22
22
  "test": "bun run check:isolate && bun test",
23
- "build": "bun build src/index.ts --target node --format esm --outdir dist --external uqr && tsc -p tsconfig.build.json",
23
+ "build": "rm -rf dist && bun build src/index.ts --target node --format esm --outdir dist --external uqr && tsc -p tsconfig.build.json",
24
24
  "prepublishOnly": "bun run build",
25
25
  "typecheck:tests": "tsc --noEmit -p tsconfig.test.json"
26
26
  },