@bigiforg/bigif 0.0.0-stage → 1.0.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.
package/README.md CHANGED
@@ -1,3 +1,17 @@
1
- # Temporary Holding Version
1
+ # @bigiforg/bigif
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ ```sh
4
+ npm install @bigiforg/bigif
5
+ ```
6
+
7
+ The official TypeScript and JavaScript SDK for the [Big If True API](https://bigif.org/developers/): Big, Disco and
8
+ Ticker with streaming, function calling, images and typed errors. No dependencies; Node 18+, Deno, Bun and browsers.
9
+
10
+ ```ts
11
+ import { BigIfTrue } from "@bigiforg/bigif";
12
+
13
+ const client = new BigIfTrue({ apiKey: process.env.BIGIF_API_KEY });
14
+ console.log(await client.ask("Hello!", { model: "big" }));
15
+ ```
16
+
17
+ Full guide: https://bigif.org/developers/sdks/#typescript
@@ -0,0 +1,266 @@
1
+ export declare const VERSION = "1.0.0";
2
+ export declare const DEFAULT_BASE_URL = "https://bigif.org/api/openai/v1";
3
+ export type Model = "big" | "big-think" | "disco" | "disco-think" | "ticker" | "ticker-think" | (string & {});
4
+ export type Role = "system" | "developer" | "user" | "assistant" | "tool";
5
+ export type ContentPart = {
6
+ type: "text";
7
+ text: string;
8
+ } | {
9
+ type: "image_url";
10
+ image_url: {
11
+ url: string;
12
+ detail?: "auto" | "low" | "high";
13
+ };
14
+ };
15
+ export interface ToolCall {
16
+ id: string;
17
+ type: "function";
18
+ function: {
19
+ name: string;
20
+ arguments: string;
21
+ };
22
+ }
23
+ export interface ChatMessage {
24
+ role: Role;
25
+ content: string | ContentPart[] | null;
26
+ name?: string;
27
+ tool_calls?: ToolCall[];
28
+ tool_call_id?: string;
29
+ }
30
+ export interface Tool {
31
+ type: "function";
32
+ function: {
33
+ name: string;
34
+ description?: string;
35
+ parameters?: Record<string, unknown>;
36
+ strict?: boolean;
37
+ };
38
+ }
39
+ export interface GenerationParams {
40
+ temperature?: number;
41
+ top_p?: number;
42
+ top_k?: number;
43
+ min_p?: number;
44
+ top_a?: number;
45
+ presence_penalty?: number;
46
+ frequency_penalty?: number;
47
+ repetition_penalty?: number;
48
+ seed?: number;
49
+ logit_bias?: Record<string, number>;
50
+ max_tokens?: number;
51
+ max_completion_tokens?: number;
52
+ stop?: string | string[];
53
+ reasoning_effort?: "none" | "minimal" | "low" | "medium" | "high" | "xhigh";
54
+ logprobs?: boolean;
55
+ top_logprobs?: number;
56
+ user?: string;
57
+ stream_options?: {
58
+ include_usage?: boolean;
59
+ };
60
+ }
61
+ export interface ChatCompletionParams extends GenerationParams {
62
+ model: Model;
63
+ messages: ChatMessage[];
64
+ tools?: Tool[];
65
+ tool_choice?: "auto" | "none" | "required" | {
66
+ type: "function";
67
+ function: {
68
+ name: string;
69
+ };
70
+ };
71
+ parallel_tool_calls?: boolean;
72
+ response_format?: {
73
+ type: "text" | "json_object" | "json_schema";
74
+ json_schema?: Record<string, unknown>;
75
+ };
76
+ stream?: boolean;
77
+ }
78
+ export interface CompletionParams extends GenerationParams {
79
+ model: Model;
80
+ prompt: string;
81
+ stream?: boolean;
82
+ }
83
+ export interface BillingInfo {
84
+ credits?: number;
85
+ credits_final?: boolean;
86
+ request_id?: string;
87
+ log_id?: string;
88
+ }
89
+ export interface Usage {
90
+ prompt_tokens: number;
91
+ completion_tokens: number;
92
+ total_tokens: number;
93
+ bigif?: BillingInfo;
94
+ }
95
+ export interface ChatCompletion {
96
+ id: string;
97
+ object: "chat.completion";
98
+ created: number;
99
+ model: string;
100
+ choices: {
101
+ index: number;
102
+ finish_reason: string;
103
+ logprobs: unknown;
104
+ message: {
105
+ role: "assistant";
106
+ content: string | null;
107
+ reasoning_content?: string;
108
+ tool_calls?: ToolCall[];
109
+ };
110
+ }[];
111
+ usage: Usage;
112
+ bigif?: BillingInfo;
113
+ }
114
+ export interface ChatCompletionChunk {
115
+ id: string;
116
+ object: "chat.completion.chunk";
117
+ created: number;
118
+ model: string;
119
+ usage?: Usage;
120
+ choices: {
121
+ index: number;
122
+ finish_reason: string | null;
123
+ logprobs?: unknown;
124
+ delta: {
125
+ role?: string;
126
+ content?: string;
127
+ reasoning_content?: string;
128
+ tool_calls?: {
129
+ index: number;
130
+ id?: string;
131
+ type?: "function";
132
+ function?: {
133
+ name?: string;
134
+ arguments?: string;
135
+ };
136
+ }[];
137
+ };
138
+ }[];
139
+ }
140
+ export interface TextCompletion {
141
+ id: string;
142
+ object: "text_completion";
143
+ created: number;
144
+ model: string;
145
+ choices: {
146
+ text: string;
147
+ index: number;
148
+ finish_reason: string | null;
149
+ }[];
150
+ usage?: Usage;
151
+ bigif?: BillingInfo;
152
+ }
153
+ export interface ModelInfo {
154
+ id: string;
155
+ object: "model";
156
+ created: number;
157
+ owned_by: string;
158
+ name?: string;
159
+ description?: string;
160
+ }
161
+ export interface ImageParams {
162
+ prompt: string;
163
+ model?: Model;
164
+ size?: string;
165
+ quality?: "standard" | "hd" | "high" | "max";
166
+ n?: 1;
167
+ }
168
+ export interface ImageResponse {
169
+ created: number;
170
+ data: {
171
+ b64_json?: string;
172
+ url?: string;
173
+ revised_prompt?: string;
174
+ }[];
175
+ }
176
+ export interface ClientOptions {
177
+ apiKey?: string;
178
+ baseURL?: string;
179
+ timeoutMs?: number;
180
+ maxRetries?: number;
181
+ appName?: string;
182
+ fetch?: typeof fetch;
183
+ }
184
+ /** An error from the API, in OpenAI's shape (status 0 for a network failure). */
185
+ export declare class BigIfTrueError extends Error {
186
+ status: number;
187
+ type: string;
188
+ code: string | null;
189
+ param: string | null;
190
+ requestId: string;
191
+ body: unknown;
192
+ constructor(message: string, init?: {
193
+ status?: number;
194
+ type?: string;
195
+ code?: string | null;
196
+ param?: string | null;
197
+ requestId?: string;
198
+ body?: unknown;
199
+ });
200
+ }
201
+ /** A streamed completion: `for await (const chunk of stream)`. `text` and `usage` fill in as it goes. */
202
+ export declare class Stream<T> implements AsyncIterable<T> {
203
+ private response;
204
+ readonly controller: AbortController;
205
+ private onDone;
206
+ text: string;
207
+ usage: Usage | undefined;
208
+ readonly requestId: string;
209
+ constructor(response: Response, controller: AbortController, onDone?: () => void);
210
+ [Symbol.asyncIterator](): AsyncIterator<T>;
211
+ /** Read to the end and return the whole text. */
212
+ finalText(): Promise<string>;
213
+ abort(): void;
214
+ }
215
+ export declare class BigIfTrue {
216
+ readonly apiKey: string;
217
+ readonly baseURL: string;
218
+ readonly timeoutMs: number;
219
+ readonly maxRetries: number;
220
+ readonly appName?: string;
221
+ private fetcher;
222
+ constructor(options?: ClientOptions);
223
+ /** Low-level request: JSON in, JSON (or a Stream) out, with retries for busy or failing servers. */
224
+ request<T>(method: string, path: string, body?: unknown, stream?: boolean): Promise<T>;
225
+ readonly models: {
226
+ list: () => Promise<{
227
+ object: "list";
228
+ data: ModelInfo[];
229
+ }>;
230
+ retrieve: (model: Model) => Promise<ModelInfo>;
231
+ };
232
+ readonly chat: {
233
+ completions: {
234
+ create: {
235
+ (params: ChatCompletionParams & {
236
+ stream: true;
237
+ }): Promise<Stream<ChatCompletionChunk>>;
238
+ (params: ChatCompletionParams & {
239
+ stream?: false;
240
+ }): Promise<ChatCompletion>;
241
+ (params: ChatCompletionParams): Promise<ChatCompletion | Stream<ChatCompletionChunk>>;
242
+ };
243
+ };
244
+ };
245
+ readonly completions: {
246
+ create: {
247
+ (params: CompletionParams & {
248
+ stream: true;
249
+ }): Promise<Stream<TextCompletion>>;
250
+ (params: CompletionParams & {
251
+ stream?: false;
252
+ }): Promise<TextCompletion>;
253
+ };
254
+ };
255
+ readonly images: {
256
+ generate: (params: ImageParams) => Promise<ImageResponse>;
257
+ };
258
+ /** One question, one answer: the reply's text. */
259
+ ask(prompt: string, options?: Omit<ChatCompletionParams, "messages" | "model" | "stream"> & {
260
+ model?: Model;
261
+ system?: string;
262
+ }): Promise<string>;
263
+ /** The API's OpenAPI description. */
264
+ openapi(): Promise<Record<string, unknown>>;
265
+ }
266
+ export default BigIfTrue;
package/dist/index.js ADDED
@@ -0,0 +1,151 @@
1
+ // The official TypeScript SDK for the Big If True API: Big, Disco and Ticker
2
+ // through the OpenAI-compatible endpoints at https://bigif.org/api/openai/v1.
3
+ export const VERSION = "1.0.0";
4
+ export const DEFAULT_BASE_URL = "https://bigif.org/api/openai/v1";
5
+ /** An error from the API, in OpenAI's shape (status 0 for a network failure). */
6
+ export class BigIfTrueError extends Error {
7
+ constructor(message, init = {}) {
8
+ super(message);
9
+ this.name = "BigIfTrueError";
10
+ this.status = init.status ?? 0;
11
+ this.type = init.type ?? "";
12
+ this.code = init.code ?? null;
13
+ this.param = init.param ?? null;
14
+ this.requestId = init.requestId ?? "";
15
+ this.body = init.body;
16
+ }
17
+ }
18
+ /** A streamed completion: `for await (const chunk of stream)`. `text` and `usage` fill in as it goes. */
19
+ export class Stream {
20
+ constructor(response, controller, onDone = () => { }) {
21
+ this.response = response;
22
+ this.controller = controller;
23
+ this.onDone = onDone;
24
+ this.text = "";
25
+ this.requestId = response.headers.get("x-request-id") ?? "";
26
+ }
27
+ async *[Symbol.asyncIterator]() {
28
+ const reader = this.response.body.getReader();
29
+ const decoder = new TextDecoder();
30
+ let buffer = "";
31
+ try {
32
+ for (;;) {
33
+ const { value, done } = await reader.read();
34
+ if (done)
35
+ return;
36
+ buffer += decoder.decode(value, { stream: true });
37
+ let index;
38
+ while ((index = buffer.indexOf("\n\n")) >= 0) {
39
+ const frame = buffer.slice(0, index);
40
+ buffer = buffer.slice(index + 2);
41
+ const data = frame.split("\n").filter((line) => line.startsWith("data:")).map((line) => line.slice(5).trim()).join("");
42
+ if (!data)
43
+ continue;
44
+ if (data === "[DONE]")
45
+ return;
46
+ const chunk = JSON.parse(data);
47
+ if (chunk.error)
48
+ throw new BigIfTrueError(chunk.error.message, { status: 200, type: chunk.error.type, code: chunk.error.code, body: chunk });
49
+ if (chunk.usage)
50
+ this.usage = chunk.usage;
51
+ const choice = chunk.choices?.[0];
52
+ this.text += choice?.delta?.content ?? choice?.text ?? "";
53
+ yield chunk;
54
+ }
55
+ }
56
+ }
57
+ finally {
58
+ reader.releaseLock();
59
+ this.onDone();
60
+ }
61
+ }
62
+ /** Read to the end and return the whole text. */
63
+ async finalText() { for await (const _ of this) { /* drain */ } return this.text; }
64
+ abort() { this.controller.abort(); }
65
+ }
66
+ const RETRY = new Set([408, 409, 429, 500, 502, 503, 504]);
67
+ const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
68
+ export class BigIfTrue {
69
+ constructor(options = {}) {
70
+ this.models = {
71
+ list: () => this.request("GET", "/models"),
72
+ retrieve: (model) => this.request("GET", `/models/${model}`),
73
+ };
74
+ this.chat = {
75
+ completions: {
76
+ create: ((params) => {
77
+ const body = params.stream ? { stream_options: { include_usage: true }, ...params } : params;
78
+ return this.request("POST", "/chat/completions", body, Boolean(params.stream));
79
+ }),
80
+ },
81
+ };
82
+ this.completions = {
83
+ create: ((params) => {
84
+ const body = params.stream ? { stream_options: { include_usage: true }, ...params } : params;
85
+ return this.request("POST", "/completions", body, Boolean(params.stream));
86
+ }),
87
+ };
88
+ this.images = {
89
+ generate: (params) => this.request("POST", "/images/generations", params),
90
+ };
91
+ // process.env where it exists (Node, Bun, Deno with compat); browsers pass apiKey.
92
+ const env = globalThis.process?.env ?? {};
93
+ this.apiKey = options.apiKey ?? env.BIGIF_API_KEY ?? "";
94
+ if (!this.apiKey)
95
+ throw new BigIfTrueError("No API key. Pass apiKey or set BIGIF_API_KEY. Create one at https://bigif.org/workspace/?view=api.");
96
+ this.baseURL = (options.baseURL ?? env.BIGIF_BASE_URL ?? DEFAULT_BASE_URL).replace(/\/+$/, "");
97
+ this.timeoutMs = options.timeoutMs ?? 600000;
98
+ this.maxRetries = options.maxRetries ?? 2;
99
+ this.appName = options.appName;
100
+ this.fetcher = options.fetch ?? globalThis.fetch.bind(globalThis);
101
+ }
102
+ /** Low-level request: JSON in, JSON (or a Stream) out, with retries for busy or failing servers. */
103
+ async request(method, path, body, stream = false) {
104
+ const headers = { Authorization: `Bearer ${this.apiKey}`, Accept: stream ? "text/event-stream" : "application/json" };
105
+ if (body !== undefined)
106
+ headers["Content-Type"] = "application/json";
107
+ if (this.appName)
108
+ headers["X-Title"] = this.appName;
109
+ for (let attempt = 0;; attempt++) {
110
+ const controller = new AbortController();
111
+ const timer = setTimeout(() => controller.abort(), this.timeoutMs);
112
+ timer?.unref?.();
113
+ let response;
114
+ try {
115
+ response = await this.fetcher(`${this.baseURL}${path}`, { method, headers, body: body === undefined ? undefined : JSON.stringify(body), signal: controller.signal });
116
+ }
117
+ catch (error) {
118
+ clearTimeout(timer);
119
+ if (attempt < this.maxRetries) {
120
+ await sleep(500 * 2 ** attempt);
121
+ continue;
122
+ }
123
+ throw new BigIfTrueError(`Could not reach the Big If True API: ${error.message}`);
124
+ }
125
+ if (!stream)
126
+ clearTimeout(timer);
127
+ if (response.ok)
128
+ return (stream ? new Stream(response, controller, () => clearTimeout(timer)) : await response.json());
129
+ clearTimeout(timer);
130
+ const data = await response.json().catch(() => ({ error: { message: `HTTP ${response.status}` } }));
131
+ const error = data?.error ?? {};
132
+ if (RETRY.has(response.status) && attempt < this.maxRetries && error.type !== "insufficient_quota") {
133
+ const after = Number(response.headers.get("retry-after"));
134
+ await sleep(Number.isFinite(after) && after > 0 ? Math.min(after, 30) * 1000 : 500 * 2 ** attempt);
135
+ continue;
136
+ }
137
+ throw new BigIfTrueError(error.message ?? `HTTP ${response.status}`, { status: response.status, type: error.type, code: error.code, param: error.param,
138
+ requestId: response.headers.get("x-request-id") ?? "", body: data });
139
+ }
140
+ }
141
+ /** One question, one answer: the reply's text. */
142
+ async ask(prompt, options = {}) {
143
+ const { model = "big", system, ...params } = options;
144
+ const messages = [...(system ? [{ role: "system", content: system }] : []), { role: "user", content: prompt }];
145
+ const reply = await this.chat.completions.create({ ...params, model, messages, stream: false });
146
+ return reply.choices[0]?.message.content ?? "";
147
+ }
148
+ /** The API's OpenAPI description. */
149
+ openapi() { return this.request("GET", "/openapi.json"); }
150
+ }
151
+ export default BigIfTrue;
package/package.json CHANGED
@@ -1,6 +1,15 @@
1
1
  {
2
2
  "name": "@bigiforg/bigif",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.0.0",
4
+ "description": "The official TypeScript and JavaScript SDK for the Big If True API (Big, Disco and Ticker, OpenAI-compatible).",
5
+ "type": "module",
6
+ "main": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": { ".": { "types": "./dist/index.d.ts", "import": "./dist/index.js" } },
9
+ "files": ["dist", "README.md"],
10
+ "engines": { "node": ">=18" },
11
+ "license": "MIT",
12
+ "homepage": "https://bigif.org/developers/sdks/#typescript",
13
+ "scripts": { "build": "tsc", "test": "node --test test/client.test.js" },
14
+ "devDependencies": { "typescript": "5.6.3" }
15
+ }