@seanhogg/builderforce-sdk 0.2.0 → 0.3.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/dist/index.d.ts CHANGED
@@ -1,21 +1,131 @@
1
- declare const AI_USE_CASES: readonly ["ide.chat", "ide.code_complete", "training.dataset_generate", "training.dataset_evaluate", "agent.inference", "coder.code", "coder.review", "coder.test", "coder.debug", "coder.refactor", "coder.document", "coder.architect", "coach.chat", "coach.insight", "coach.classify", "studio.compose", "studio.script", "studio.brief", "pitch_deck.generate", "investor.update", "ask.general", "tool.classify_email", "tool.categorize_expense", "tool.contract_analyze", "tool.competitor_scan", "tool.feature_score", "tool.market_research", "tool.health_score", "tool.journey_insight", "vision.describe", "ocr.extract", "embed.text", "match", "match_tailor", "match_insights", "resume_roast", "skill_extract", "job_parser", "autofill", "article_writer", "studio_script", "studio_edit_script", "studio_misc", "linkedin_post", "interview_questions", "interview_analyze", "chat", "career", "discovery", "dashboard_summary"];
2
- type AIUseCase = (typeof AI_USE_CASES)[number];
3
- declare function isAIUseCase(value: string): value is AIUseCase;
4
-
5
1
  type ChatRole = 'system' | 'user' | 'assistant' | 'tool';
2
+ interface TextContentPart {
3
+ type: 'text';
4
+ text: string;
5
+ }
6
+ interface ImageUrlContentPart {
7
+ type: 'image_url';
8
+ image_url: {
9
+ /** Either an `https://...` URL or a `data:image/...;base64,...` data URI. */
10
+ url: string;
11
+ /** Image detail hint — `'low' | 'high' | 'auto'`. Vendor-specific behaviour. */
12
+ detail?: 'low' | 'high' | 'auto';
13
+ };
14
+ }
15
+ type ContentPart = TextContentPart | ImageUrlContentPart;
16
+ interface FunctionDefinition {
17
+ name: string;
18
+ description?: string;
19
+ /** JSON-Schema describing the function's argument shape. */
20
+ parameters?: Record<string, unknown>;
21
+ }
22
+ interface ToolSpec {
23
+ type: 'function';
24
+ function: FunctionDefinition;
25
+ }
26
+ interface ToolCallFunction {
27
+ name: string;
28
+ /** JSON-encoded argument string. Caller is responsible for `JSON.parse`. */
29
+ arguments: string;
30
+ }
31
+ interface ToolCall {
32
+ id: string;
33
+ type: 'function';
34
+ function: ToolCallFunction;
35
+ }
36
+ type ToolChoice = 'auto' | 'none' | 'required' | {
37
+ type: 'function';
38
+ function: {
39
+ name: string;
40
+ };
41
+ };
42
+ /** Streaming-mode tool-call delta. Each chunk carries an `index` so callers
43
+ * can stitch fragments together by call slot. */
44
+ interface ToolCallDelta {
45
+ index: number;
46
+ id?: string;
47
+ type?: 'function';
48
+ function?: {
49
+ name?: string;
50
+ /** Partial JSON-encoded arguments — concatenate across deltas to reassemble. */
51
+ arguments?: string;
52
+ };
53
+ }
54
+ interface JsonSchemaSpec {
55
+ name: string;
56
+ description?: string;
57
+ /** JSON-Schema document. Use `strict: true` for vendor-side conformance retry. */
58
+ schema: Record<string, unknown>;
59
+ strict?: boolean;
60
+ }
61
+ type ResponseFormat = {
62
+ type: 'text';
63
+ } | {
64
+ type: 'json_object';
65
+ } | {
66
+ type: 'json_schema';
67
+ json_schema: JsonSchemaSpec;
68
+ };
6
69
  interface ChatMessage {
7
70
  role: ChatRole;
8
- content: string;
71
+ /** String for plain text. `ContentPart[]` for vision (text + image_url). `null`
72
+ * is allowed on assistant turns that only carry `tool_calls`. */
73
+ content: string | ContentPart[] | null;
9
74
  name?: string;
75
+ /** Assistant-turn tool calls. Caller executes each and replies with a `tool` message
76
+ * whose `tool_call_id` matches `tool_calls[i].id`. */
77
+ tool_calls?: ToolCall[];
78
+ /** Required on `role: 'tool'` messages — the id of the assistant tool call this
79
+ * message is responding to. */
80
+ tool_call_id?: string;
81
+ }
82
+ interface PerCallOptions {
83
+ /** Override the client-level timeout for just this call. Useful when use
84
+ * cases have a wide latency spread (autofill ~2s vs `career_360` ~30s). */
85
+ timeoutMs?: number;
86
+ /** Caller-supplied AbortSignal for user-cancellable generation. Fires alongside
87
+ * the SDK's internal timeout signal — whichever fires first wins. */
88
+ signal?: AbortSignal;
89
+ /** Sent as `Idempotency-Key` header — gateway dedupes retries within its TTL. */
90
+ idempotencyKey?: string;
10
91
  }
11
- interface ChatCompletionCreateParams {
92
+ interface ChatCompletionCreateParams extends PerCallOptions {
93
+ /**
94
+ * Model **hint** (not a hard pin). When set, the gateway puts this id at the
95
+ * head of its candidate chain so it's tried first — but the gateway retains
96
+ * the right to substitute on cooldown, vendor outage, or plan-tier mismatch.
97
+ * The actual model used is reported via `_builderforce.resolvedModel`; check
98
+ * it if you need to detect substitution.
99
+ *
100
+ * Vendor prefixes (`openrouter/<id>`, `cerebras/<id>`, `ollama/<id>`) route
101
+ * to the named vendor when that model is selected. Bare ids fall back to
102
+ * catalog lookup.
103
+ *
104
+ * When unset, the gateway picks from the tenant-plan model pool with
105
+ * shape-based reordering (tools / response_format / vision content blocks).
106
+ */
12
107
  model?: string;
13
108
  messages: ChatMessage[];
14
109
  stream?: boolean;
15
- useCase?: AIUseCase;
16
110
  temperature?: number;
17
111
  max_tokens?: number;
18
112
  top_p?: number;
113
+ /** Tool / function-calling spec. */
114
+ tools?: ToolSpec[];
115
+ tool_choice?: ToolChoice;
116
+ /** Structured-output mode. `'json_object'` is loose JSON; `'json_schema'` requests
117
+ * gateway-side schema validation with retry across the failover chain. */
118
+ response_format?: ResponseFormat;
119
+ /**
120
+ * Opaque telemetry slug. The gateway treats this as a free-form string —
121
+ * persisted to `llm_usage_log.use_case` and echoed back in `_builderforce.useCase`
122
+ * for confirmation, but **never used for routing**. The taxonomy is yours.
123
+ */
124
+ useCase?: string;
125
+ /** Free-form key/value pairs persisted to `llm_usage_log.metadata` for billing
126
+ * trace-back ({ toolRunId, sessionId, userId, featureKey, … }). Echoed back
127
+ * in `_builderforce.metadata`. Not forwarded to vendors. */
128
+ metadata?: Record<string, string>;
19
129
  [key: string]: unknown;
20
130
  }
21
131
  interface ChatCompletionChunk {
@@ -27,7 +137,8 @@ interface ChatCompletionChunk {
27
137
  index?: number;
28
138
  delta?: {
29
139
  role?: ChatRole;
30
- content?: string;
140
+ content?: string | null;
141
+ tool_calls?: ToolCallDelta[];
31
142
  };
32
143
  finish_reason?: string | null;
33
144
  }>;
@@ -46,8 +157,10 @@ interface ChatCompletionResponse {
46
157
  index?: number;
47
158
  message?: {
48
159
  role?: ChatRole;
49
- content?: string;
160
+ content?: string | null;
161
+ tool_calls?: ToolCall[];
50
162
  };
163
+ /** `'stop' | 'length' | 'tool_calls' | 'content_filter' | …` */
51
164
  finish_reason?: string | null;
52
165
  }>;
53
166
  usage?: {
@@ -56,11 +169,21 @@ interface ChatCompletionResponse {
56
169
  total_tokens?: number;
57
170
  };
58
171
  _builderforce?: {
172
+ /** The model the gateway dispatched against. Equals `request.model` when caller pinned. */
59
173
  resolvedModel?: string;
174
+ /** How many vendor retries happened inside the failover chain. */
60
175
  retries?: number;
61
176
  pool?: number;
62
177
  product?: string;
63
178
  effectivePlan?: string;
179
+ /** Number of vendor retries the gateway performed for json_schema conformance. */
180
+ schemaRetries?: number;
181
+ /** Echo of `request.useCase` (opaque telemetry slug). */
182
+ useCase?: string;
183
+ /** Echo of `request.metadata` for caller-side billing trace-back. */
184
+ metadata?: Record<string, string>;
185
+ /** Mirror of the `x-request-id` response header. */
186
+ requestId?: string;
64
187
  };
65
188
  [key: string]: unknown;
66
189
  }
@@ -124,6 +247,36 @@ interface UsageResponse {
124
247
  interface UsageGetParams {
125
248
  days?: number;
126
249
  }
250
+ interface EmbeddingsCreateParams extends PerCallOptions {
251
+ model?: string;
252
+ /** Single string or array of strings to embed. */
253
+ input: string | string[];
254
+ /** Opaque telemetry slug — same semantics as chat. */
255
+ useCase?: string;
256
+ /** Free-form attribution metadata (same semantics as chat). */
257
+ metadata?: Record<string, string>;
258
+ [key: string]: unknown;
259
+ }
260
+ interface EmbeddingObject {
261
+ object: 'embedding';
262
+ index: number;
263
+ embedding: number[];
264
+ }
265
+ interface EmbeddingsResponse {
266
+ object: 'list';
267
+ data: EmbeddingObject[];
268
+ model: string;
269
+ usage?: {
270
+ prompt_tokens?: number;
271
+ total_tokens?: number;
272
+ };
273
+ _builderforce?: {
274
+ resolvedModel?: string;
275
+ retries?: number;
276
+ product?: string;
277
+ };
278
+ [key: string]: unknown;
279
+ }
127
280
 
128
281
  declare class BuilderforceApiError extends Error {
129
282
  readonly status: number;
@@ -136,18 +289,34 @@ interface HttpClientOptions {
136
289
  apiKey: string;
137
290
  baseUrl: string;
138
291
  fetchFn?: typeof fetch;
292
+ /** Default per-request timeout in ms. Overridable per call. */
293
+ timeoutMs?: number;
294
+ }
295
+ /** Per-request overrides — passed by the API layer, not by SDK consumers directly. */
296
+ interface RequestOptions {
297
+ /** Override the client default timeout for just this request. */
139
298
  timeoutMs?: number;
299
+ /** Caller-provided AbortSignal. Linked together with the SDK's internal timeout
300
+ * signal — whichever fires first aborts the request. */
301
+ signal?: AbortSignal;
302
+ /** Extra headers to merge in (e.g. `Idempotency-Key`). */
303
+ headers?: Record<string, string>;
140
304
  }
141
305
  declare class HttpClient {
142
306
  private readonly apiKey;
143
307
  private readonly baseUrl;
144
308
  private readonly fetchFn;
145
- private readonly timeoutMs;
309
+ private readonly defaultTimeoutMs;
146
310
  constructor(options: HttpClientOptions);
147
- getJson<T>(path: string): Promise<T>;
148
- postJson<T>(path: string, body: unknown): Promise<T>;
149
- postRaw(path: string, body: unknown): Promise<Response>;
150
- private authHeaders;
311
+ getJson<T>(path: string, options?: RequestOptions): Promise<T>;
312
+ postJson<T>(path: string, body: unknown, options?: RequestOptions): Promise<T>;
313
+ postRaw(path: string, body: unknown, options?: RequestOptions): Promise<Response>;
314
+ private mergeHeaders;
315
+ /**
316
+ * Wrap a fetch in a combined abort signal: an internal timeout AND any
317
+ * caller-provided signal. Either firing aborts the request. Single source of
318
+ * abort plumbing — every method routes through here (DRY).
319
+ */
151
320
  private fetchWithTimeout;
152
321
  private parseJsonResponse;
153
322
  private toApiError;
@@ -170,6 +339,19 @@ declare class ChatCompletionsApi {
170
339
  }): Promise<ChatCompletionResponse>;
171
340
  }
172
341
 
342
+ declare class EmbeddingsApi {
343
+ private readonly http;
344
+ constructor(http: HttpClient);
345
+ /**
346
+ * Create one or more text embeddings.
347
+ *
348
+ * NOTE: As of v0.3.0 the gateway accepts the request shape but the underlying
349
+ * vendor wiring is still being rolled out — calls may currently 503 with
350
+ * `code: 'embeddings_not_wired'`. Track gateway PRD §6.7.
351
+ */
352
+ create(params: EmbeddingsCreateParams): Promise<EmbeddingsResponse>;
353
+ }
354
+
173
355
  declare class ModelsApi {
174
356
  private readonly http;
175
357
  constructor(http: HttpClient);
@@ -186,15 +368,18 @@ interface BuilderforceClientOptions {
186
368
  apiKey: string;
187
369
  baseUrl?: string;
188
370
  fetch?: typeof fetch;
371
+ /** Default request timeout in ms (default 60_000). Per-call override available
372
+ * via `chat.completions.create({ timeoutMs })` and `embeddings.create({ timeoutMs })`. */
189
373
  timeoutMs?: number;
190
374
  }
191
375
  declare class BuilderforceClient {
192
376
  readonly chat: {
193
377
  completions: ChatCompletionsApi;
194
378
  };
379
+ readonly embeddings: EmbeddingsApi;
195
380
  readonly models: ModelsApi;
196
381
  readonly usage: UsageApi;
197
382
  constructor(options: BuilderforceClientOptions);
198
383
  }
199
384
 
200
- export { type AIUseCase, AI_USE_CASES, BuilderforceApiError, BuilderforceClient, type BuilderforceClientOptions, type ChatCompletionChunk, type ChatCompletionCreateParams, type ChatCompletionResponse, ChatCompletionStream, type ChatMessage, type ChatRole, type ModelsListResponse, type UsageByDay, type UsageByModel, type UsageByUser, type UsageGetParams, type UsageResponse, isAIUseCase };
385
+ export { BuilderforceApiError, BuilderforceClient, type BuilderforceClientOptions, type ChatCompletionChunk, type ChatCompletionCreateParams, type ChatCompletionResponse, ChatCompletionStream, type ChatMessage, type ChatRole, type ContentPart, type EmbeddingObject, EmbeddingsApi, type EmbeddingsCreateParams, type EmbeddingsResponse, type FunctionDefinition, type ImageUrlContentPart, type JsonSchemaSpec, type ModelsListResponse, type PerCallOptions, type ResponseFormat, type TextContentPart, type ToolCall, type ToolCallDelta, type ToolCallFunction, type ToolChoice, type ToolSpec, type UsageByDay, type UsageByModel, type UsageByUser, type UsageGetParams, type UsageResponse };
package/dist/index.mjs CHANGED
@@ -42,20 +42,66 @@ var ChatCompletionStream = class {
42
42
  return full;
43
43
  }
44
44
  };
45
+ function splitTransportOptions(params) {
46
+ const { timeoutMs, signal, idempotencyKey, ...rest } = params;
47
+ const headers = {};
48
+ if (idempotencyKey) headers["Idempotency-Key"] = idempotencyKey;
49
+ return {
50
+ body: rest,
51
+ request: {
52
+ timeoutMs,
53
+ signal,
54
+ ...Object.keys(headers).length > 0 ? { headers } : {}
55
+ }
56
+ };
57
+ }
45
58
  var ChatCompletionsApi = class {
46
59
  http;
47
60
  constructor(http) {
48
61
  this.http = http;
49
62
  }
50
63
  async create(params) {
64
+ const { body, request } = splitTransportOptions(params);
51
65
  if (params.stream) {
52
- const response = await this.http.postRaw("/llm/v1/chat/completions", params);
66
+ const response = await this.http.postRaw("/llm/v1/chat/completions", body, request);
53
67
  if (!response.body) {
54
68
  throw new Error("Streaming response body is missing");
55
69
  }
56
70
  return new ChatCompletionStream(response.body);
57
71
  }
58
- return this.http.postJson("/llm/v1/chat/completions", params);
72
+ return this.http.postJson("/llm/v1/chat/completions", body, request);
73
+ }
74
+ };
75
+
76
+ // src/application/EmbeddingsApi.ts
77
+ function splitTransportOptions2(params) {
78
+ const { timeoutMs, signal, idempotencyKey, ...rest } = params;
79
+ const headers = {};
80
+ if (idempotencyKey) headers["Idempotency-Key"] = idempotencyKey;
81
+ return {
82
+ body: rest,
83
+ request: {
84
+ timeoutMs,
85
+ signal,
86
+ ...Object.keys(headers).length > 0 ? { headers } : {}
87
+ }
88
+ };
89
+ }
90
+ var EmbeddingsApi = class {
91
+ http;
92
+ constructor(http) {
93
+ this.http = http;
94
+ }
95
+ /**
96
+ * Create one or more text embeddings.
97
+ *
98
+ * NOTE: As of v0.3.0 the gateway accepts the request shape but the underlying
99
+ * vendor wiring is still being rolled out — calls may currently 503 with
100
+ * `code: 'embeddings_not_wired'`. Track gateway PRD §6.7.
101
+ */
102
+ create(params) {
103
+ const { body, request } = splitTransportOptions2(params);
104
+ return this.http.postJson("/llm/v1/embeddings", body, request);
59
105
  }
60
106
  };
61
107
 
@@ -101,62 +147,68 @@ var HttpClient = class {
101
147
  apiKey;
102
148
  baseUrl;
103
149
  fetchFn;
104
- timeoutMs;
150
+ defaultTimeoutMs;
105
151
  constructor(options) {
106
152
  this.apiKey = options.apiKey;
107
153
  this.baseUrl = options.baseUrl.replace(/\/$/, "");
108
154
  this.fetchFn = options.fetchFn ?? fetch;
109
- this.timeoutMs = options.timeoutMs ?? 6e4;
155
+ this.defaultTimeoutMs = options.timeoutMs ?? 6e4;
110
156
  }
111
- async getJson(path) {
157
+ async getJson(path, options) {
112
158
  const res = await this.fetchWithTimeout(`${this.baseUrl}${path}`, {
113
159
  method: "GET",
114
- headers: this.authHeaders()
115
- });
160
+ headers: this.mergeHeaders(options)
161
+ }, options);
116
162
  return this.parseJsonResponse(res);
117
163
  }
118
- async postJson(path, body) {
164
+ async postJson(path, body, options) {
119
165
  const res = await this.fetchWithTimeout(`${this.baseUrl}${path}`, {
120
166
  method: "POST",
121
- headers: {
122
- ...this.authHeaders(),
123
- "Content-Type": "application/json"
124
- },
167
+ headers: this.mergeHeaders(options, { "Content-Type": "application/json" }),
125
168
  body: JSON.stringify(body)
126
- });
169
+ }, options);
127
170
  return this.parseJsonResponse(res);
128
171
  }
129
- async postRaw(path, body) {
172
+ async postRaw(path, body, options) {
130
173
  const res = await this.fetchWithTimeout(`${this.baseUrl}${path}`, {
131
174
  method: "POST",
132
- headers: {
133
- ...this.authHeaders(),
134
- "Content-Type": "application/json"
135
- },
175
+ headers: this.mergeHeaders(options, { "Content-Type": "application/json" }),
136
176
  body: JSON.stringify(body)
137
- });
177
+ }, options);
138
178
  if (!res.ok) {
139
179
  throw await this.toApiError(res);
140
180
  }
141
181
  return res;
142
182
  }
143
- authHeaders() {
183
+ mergeHeaders(options, base) {
144
184
  return {
145
- Authorization: `Bearer ${this.apiKey}`
185
+ Authorization: `Bearer ${this.apiKey}`,
186
+ ...base ?? {},
187
+ ...options?.headers ?? {}
146
188
  };
147
189
  }
148
- async fetchWithTimeout(input, init) {
149
- const controller = new AbortController();
150
- const timeout = setTimeout(() => controller.abort(), this.timeoutMs);
190
+ /**
191
+ * Wrap a fetch in a combined abort signal: an internal timeout AND any
192
+ * caller-provided signal. Either firing aborts the request. Single source of
193
+ * abort plumbing — every method routes through here (DRY).
194
+ */
195
+ async fetchWithTimeout(input, init, options) {
196
+ const timeoutMs = options?.timeoutMs ?? this.defaultTimeoutMs;
197
+ const timeoutCtl = new AbortController();
198
+ const timer = setTimeout(() => timeoutCtl.abort(), timeoutMs);
199
+ const signal = combineSignals(timeoutCtl.signal, options?.signal);
151
200
  try {
152
- return await this.fetchFn(input, { ...init, signal: controller.signal });
201
+ return await this.fetchFn(input, { ...init, signal });
153
202
  } catch (error) {
154
- if (controller.signal.aborted) {
155
- throw new BuilderforceApiError(`Request timed out after ${this.timeoutMs}ms`, 408, "timeout");
203
+ if (timeoutCtl.signal.aborted) {
204
+ throw new BuilderforceApiError(`Request timed out after ${timeoutMs}ms`, 408, "timeout");
205
+ }
206
+ if (options?.signal?.aborted) {
207
+ throw new BuilderforceApiError("Request aborted by caller", 499, "aborted");
156
208
  }
157
209
  throw error;
158
210
  } finally {
159
- clearTimeout(timeout);
211
+ clearTimeout(timer);
160
212
  }
161
213
  }
162
214
  async parseJsonResponse(res) {
@@ -177,10 +229,28 @@ var HttpClient = class {
177
229
  }
178
230
  }
179
231
  };
232
+ function combineSignals(...signals) {
233
+ const live = signals.filter((s) => s !== void 0);
234
+ if (live.length === 1) return live[0];
235
+ const anyImpl = AbortSignal.any;
236
+ if (typeof anyImpl === "function") {
237
+ return anyImpl(live);
238
+ }
239
+ const ctl = new AbortController();
240
+ for (const s of live) {
241
+ if (s.aborted) {
242
+ ctl.abort(s.reason);
243
+ break;
244
+ }
245
+ s.addEventListener("abort", () => ctl.abort(s.reason), { once: true });
246
+ }
247
+ return ctl.signal;
248
+ }
180
249
 
181
250
  // src/BuilderforceClient.ts
182
251
  var BuilderforceClient = class {
183
252
  chat;
253
+ embeddings;
184
254
  models;
185
255
  usage;
186
256
  constructor(options) {
@@ -201,72 +271,15 @@ var BuilderforceClient = class {
201
271
  this.chat = {
202
272
  completions: new ChatCompletionsApi(http)
203
273
  };
274
+ this.embeddings = new EmbeddingsApi(http);
204
275
  this.models = new ModelsApi(http);
205
276
  this.usage = new UsageApi(http);
206
277
  }
207
278
  };
208
-
209
- // src/domain/aiUseCases.ts
210
- var AI_USE_CASES = [
211
- "ide.chat",
212
- "ide.code_complete",
213
- "training.dataset_generate",
214
- "training.dataset_evaluate",
215
- "agent.inference",
216
- "coder.code",
217
- "coder.review",
218
- "coder.test",
219
- "coder.debug",
220
- "coder.refactor",
221
- "coder.document",
222
- "coder.architect",
223
- "coach.chat",
224
- "coach.insight",
225
- "coach.classify",
226
- "studio.compose",
227
- "studio.script",
228
- "studio.brief",
229
- "pitch_deck.generate",
230
- "investor.update",
231
- "ask.general",
232
- "tool.classify_email",
233
- "tool.categorize_expense",
234
- "tool.contract_analyze",
235
- "tool.competitor_scan",
236
- "tool.feature_score",
237
- "tool.market_research",
238
- "tool.health_score",
239
- "tool.journey_insight",
240
- "vision.describe",
241
- "ocr.extract",
242
- "embed.text",
243
- "match",
244
- "match_tailor",
245
- "match_insights",
246
- "resume_roast",
247
- "skill_extract",
248
- "job_parser",
249
- "autofill",
250
- "article_writer",
251
- "studio_script",
252
- "studio_edit_script",
253
- "studio_misc",
254
- "linkedin_post",
255
- "interview_questions",
256
- "interview_analyze",
257
- "chat",
258
- "career",
259
- "discovery",
260
- "dashboard_summary"
261
- ];
262
- function isAIUseCase(value) {
263
- return AI_USE_CASES.includes(value);
264
- }
265
279
  export {
266
- AI_USE_CASES,
267
280
  BuilderforceApiError,
268
281
  BuilderforceClient,
269
282
  ChatCompletionStream,
270
- isAIUseCase
283
+ EmbeddingsApi
271
284
  };
272
285
  //# sourceMappingURL=index.mjs.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/infrastructure/sse.ts","../src/application/ChatCompletionsApi.ts","../src/application/ModelsApi.ts","../src/application/UsageApi.ts","../src/infrastructure/httpClient.ts","../src/BuilderforceClient.ts","../src/domain/aiUseCases.ts"],"sourcesContent":["export async function* parseSseJson<T>(\r\n stream: ReadableStream<Uint8Array>,\r\n): AsyncGenerator<T, void, unknown> {\r\n const reader = stream.getReader();\r\n const decoder = new TextDecoder();\r\n let buffer = '';\r\n\r\n while (true) {\r\n const { done, value } = await reader.read();\r\n if (done) break;\r\n\r\n buffer += decoder.decode(value, { stream: true });\r\n const lines = buffer.split('\\n');\r\n buffer = lines.pop() ?? '';\r\n\r\n for (const line of lines) {\r\n const trimmed = line.trim();\r\n if (!trimmed.startsWith('data: ')) continue;\r\n\r\n const data = trimmed.slice(6).trim();\r\n if (data === '[DONE]') return;\r\n\r\n try {\r\n yield JSON.parse(data) as T;\r\n } catch {\r\n // Skip malformed chunks instead of breaking the stream.\r\n }\r\n }\r\n }\r\n}\r\n","import type { ChatCompletionChunk, ChatCompletionCreateParams, ChatCompletionResponse } from '../domain/types';\r\nimport { HttpClient } from '../infrastructure/httpClient';\r\nimport { parseSseJson } from '../infrastructure/sse';\r\n\r\nexport class ChatCompletionStream implements AsyncIterable<ChatCompletionChunk> {\r\n private readonly stream: ReadableStream<Uint8Array>;\r\n\r\n constructor(stream: ReadableStream<Uint8Array>) {\r\n this.stream = stream;\r\n }\r\n\r\n [Symbol.asyncIterator](): AsyncIterator<ChatCompletionChunk, void, unknown> {\r\n return parseSseJson<ChatCompletionChunk>(this.stream);\r\n }\r\n\r\n async toText(): Promise<string> {\r\n let full = '';\r\n for await (const chunk of this) {\r\n const delta = chunk.choices?.[0]?.delta?.content;\r\n if (typeof delta === 'string') {\r\n full += delta;\r\n }\r\n }\r\n return full;\r\n }\r\n}\r\n\r\nexport class ChatCompletionsApi {\r\n private readonly http: HttpClient;\r\n\r\n constructor(http: HttpClient) {\r\n this.http = http;\r\n }\r\n\r\n async create(params: ChatCompletionCreateParams & { stream: true }): Promise<ChatCompletionStream>;\r\n async create(params: ChatCompletionCreateParams & { stream?: false | undefined }): Promise<ChatCompletionResponse>;\r\n async create(\r\n params: ChatCompletionCreateParams,\r\n ): Promise<ChatCompletionResponse | ChatCompletionStream> {\r\n if (params.stream) {\r\n const response = await this.http.postRaw('/llm/v1/chat/completions', params);\r\n if (!response.body) {\r\n throw new Error('Streaming response body is missing');\r\n }\r\n return new ChatCompletionStream(response.body);\r\n }\r\n\r\n return this.http.postJson<ChatCompletionResponse>('/llm/v1/chat/completions', params);\r\n }\r\n}\r\n","import type { ModelsListResponse } from '../domain/types';\r\nimport { HttpClient } from '../infrastructure/httpClient';\r\n\r\nexport class ModelsApi {\r\n private readonly http: HttpClient;\r\n\r\n constructor(http: HttpClient) {\r\n this.http = http;\r\n }\r\n\r\n list(): Promise<ModelsListResponse> {\r\n return this.http.getJson<ModelsListResponse>('/llm/v1/models');\r\n }\r\n}\r\n","import type { UsageGetParams, UsageResponse } from '../domain/types';\r\nimport { HttpClient } from '../infrastructure/httpClient';\r\n\r\nexport class UsageApi {\r\n private readonly http: HttpClient;\r\n\r\n constructor(http: HttpClient) {\r\n this.http = http;\r\n }\r\n\r\n get(params: UsageGetParams = {}): Promise<UsageResponse> {\r\n const query = typeof params.days === 'number' ? `?days=${encodeURIComponent(String(params.days))}` : '';\r\n return this.http.getJson<UsageResponse>(`/llm/v1/usage${query}`);\r\n }\r\n}\r\n","export class BuilderforceApiError extends Error {\r\n public readonly status: number;\r\n public readonly code?: string;\r\n public readonly details?: unknown;\r\n public readonly requestId?: string;\r\n\r\n constructor(message: string, status: number, code?: string, details?: unknown, requestId?: string) {\r\n super(message);\r\n this.name = 'BuilderforceApiError';\r\n this.status = status;\r\n this.code = code;\r\n this.details = details;\r\n this.requestId = requestId;\r\n }\r\n}\r\n\r\nexport interface HttpClientOptions {\r\n apiKey: string;\r\n baseUrl: string;\r\n fetchFn?: typeof fetch;\r\n timeoutMs?: number;\r\n}\r\n\r\nexport class HttpClient {\r\n private readonly apiKey: string;\r\n private readonly baseUrl: string;\r\n private readonly fetchFn: typeof fetch;\r\n private readonly timeoutMs: number;\r\n\r\n constructor(options: HttpClientOptions) {\r\n this.apiKey = options.apiKey;\r\n this.baseUrl = options.baseUrl.replace(/\\/$/, '');\r\n this.fetchFn = options.fetchFn ?? fetch;\r\n this.timeoutMs = options.timeoutMs ?? 60_000;\r\n }\r\n\r\n async getJson<T>(path: string): Promise<T> {\r\n const res = await this.fetchWithTimeout(`${this.baseUrl}${path}`, {\r\n method: 'GET',\r\n headers: this.authHeaders(),\r\n });\r\n return this.parseJsonResponse<T>(res);\r\n }\r\n\r\n async postJson<T>(path: string, body: unknown): Promise<T> {\r\n const res = await this.fetchWithTimeout(`${this.baseUrl}${path}`, {\r\n method: 'POST',\r\n headers: {\r\n ...this.authHeaders(),\r\n 'Content-Type': 'application/json',\r\n },\r\n body: JSON.stringify(body),\r\n });\r\n return this.parseJsonResponse<T>(res);\r\n }\r\n\r\n async postRaw(path: string, body: unknown): Promise<Response> {\r\n const res = await this.fetchWithTimeout(`${this.baseUrl}${path}`, {\r\n method: 'POST',\r\n headers: {\r\n ...this.authHeaders(),\r\n 'Content-Type': 'application/json',\r\n },\r\n body: JSON.stringify(body),\r\n });\r\n if (!res.ok) {\r\n throw await this.toApiError(res);\r\n }\r\n return res;\r\n }\r\n\r\n private authHeaders(): Record<string, string> {\r\n return {\r\n Authorization: `Bearer ${this.apiKey}`,\r\n };\r\n }\r\n\r\n private async fetchWithTimeout(input: RequestInfo | URL, init: RequestInit): Promise<Response> {\r\n const controller = new AbortController();\r\n const timeout = setTimeout(() => controller.abort(), this.timeoutMs);\r\n try {\r\n return await this.fetchFn(input, { ...init, signal: controller.signal });\r\n } catch (error) {\r\n if (controller.signal.aborted) {\r\n throw new BuilderforceApiError(`Request timed out after ${this.timeoutMs}ms`, 408, 'timeout');\r\n }\r\n throw error;\r\n } finally {\r\n clearTimeout(timeout);\r\n }\r\n }\r\n\r\n private async parseJsonResponse<T>(res: Response): Promise<T> {\r\n if (!res.ok) {\r\n throw await this.toApiError(res);\r\n }\r\n return res.json() as Promise<T>;\r\n }\r\n\r\n private async toApiError(res: Response): Promise<BuilderforceApiError> {\r\n const fallback = `Request failed (${res.status})`;\r\n const requestId = res.headers.get('x-request-id') ?? undefined;\r\n try {\r\n const payload = await res.json() as { error?: string; code?: string; details?: unknown };\r\n return new BuilderforceApiError(payload.error ?? fallback, res.status, payload.code, payload.details, requestId);\r\n } catch {\r\n const text = await res.text().catch(() => '');\r\n return new BuilderforceApiError(text || fallback, res.status, undefined, undefined, requestId);\r\n }\r\n }\r\n}\r\n","import { ChatCompletionsApi } from './application/ChatCompletionsApi';\r\nimport { ModelsApi } from './application/ModelsApi';\r\nimport { UsageApi } from './application/UsageApi';\r\nimport { BuilderforceApiError, HttpClient } from './infrastructure/httpClient';\r\n\r\nexport interface BuilderforceClientOptions {\r\n apiKey: string;\r\n baseUrl?: string;\r\n fetch?: typeof fetch;\r\n timeoutMs?: number;\r\n}\r\n\r\nexport class BuilderforceClient {\r\n public readonly chat: {\r\n completions: ChatCompletionsApi;\r\n };\r\n public readonly models: ModelsApi;\r\n public readonly usage: UsageApi;\r\n\r\n constructor(options: BuilderforceClientOptions) {\r\n const apiKey = options.apiKey?.trim();\r\n if (!apiKey) {\r\n throw new BuilderforceApiError(\r\n 'BuilderforceClient requires a non-empty apiKey',\r\n 400,\r\n 'missing_api_key',\r\n );\r\n }\r\n\r\n const http = new HttpClient({\r\n apiKey,\r\n baseUrl: options.baseUrl ?? 'https://api.builderforce.ai',\r\n fetchFn: options.fetch,\r\n timeoutMs: options.timeoutMs,\r\n });\r\n\r\n this.chat = {\r\n completions: new ChatCompletionsApi(http),\r\n };\r\n this.models = new ModelsApi(http);\r\n this.usage = new UsageApi(http);\r\n }\r\n}\r\n","// Keep this list aligned with api/src/application/llm/aiUseCases.ts.\r\nexport const AI_USE_CASES = [\r\n 'ide.chat',\r\n 'ide.code_complete',\r\n 'training.dataset_generate',\r\n 'training.dataset_evaluate',\r\n 'agent.inference',\r\n 'coder.code',\r\n 'coder.review',\r\n 'coder.test',\r\n 'coder.debug',\r\n 'coder.refactor',\r\n 'coder.document',\r\n 'coder.architect',\r\n 'coach.chat',\r\n 'coach.insight',\r\n 'coach.classify',\r\n 'studio.compose',\r\n 'studio.script',\r\n 'studio.brief',\r\n 'pitch_deck.generate',\r\n 'investor.update',\r\n 'ask.general',\r\n 'tool.classify_email',\r\n 'tool.categorize_expense',\r\n 'tool.contract_analyze',\r\n 'tool.competitor_scan',\r\n 'tool.feature_score',\r\n 'tool.market_research',\r\n 'tool.health_score',\r\n 'tool.journey_insight',\r\n 'vision.describe',\r\n 'ocr.extract',\r\n 'embed.text',\r\n 'match',\r\n 'match_tailor',\r\n 'match_insights',\r\n 'resume_roast',\r\n 'skill_extract',\r\n 'job_parser',\r\n 'autofill',\r\n 'article_writer',\r\n 'studio_script',\r\n 'studio_edit_script',\r\n 'studio_misc',\r\n 'linkedin_post',\r\n 'interview_questions',\r\n 'interview_analyze',\r\n 'chat',\r\n 'career',\r\n 'discovery',\r\n 'dashboard_summary',\r\n] as const;\r\n\r\nexport type AIUseCase = (typeof AI_USE_CASES)[number];\r\n\r\nexport function isAIUseCase(value: string): value is AIUseCase {\r\n return (AI_USE_CASES as readonly string[]).includes(value);\r\n}\r\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/infrastructure/sse.ts","../src/application/ChatCompletionsApi.ts","../src/application/EmbeddingsApi.ts","../src/application/ModelsApi.ts","../src/application/UsageApi.ts","../src/infrastructure/httpClient.ts","../src/BuilderforceClient.ts"],"sourcesContent":["export async function* parseSseJson<T>(\n stream: ReadableStream<Uint8Array>,\n): AsyncGenerator<T, void, unknown> {\n const reader = stream.getReader();\n const decoder = new TextDecoder();\n let buffer = '';\n\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n\n buffer += decoder.decode(value, { stream: true });\n const lines = buffer.split('\\n');\n buffer = lines.pop() ?? '';\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (!trimmed.startsWith('data: ')) continue;\n\n const data = trimmed.slice(6).trim();\n if (data === '[DONE]') return;\n\n try {\n yield JSON.parse(data) as T;\n } catch {\n // Skip malformed chunks instead of breaking the stream.\n }\n }\n }\n}\n","import type { ChatCompletionChunk, ChatCompletionCreateParams, ChatCompletionResponse } from '../domain/types';\nimport { HttpClient, type RequestOptions } from '../infrastructure/httpClient';\nimport { parseSseJson } from '../infrastructure/sse';\n\nexport class ChatCompletionStream implements AsyncIterable<ChatCompletionChunk> {\n private readonly stream: ReadableStream<Uint8Array>;\n\n constructor(stream: ReadableStream<Uint8Array>) {\n this.stream = stream;\n }\n\n [Symbol.asyncIterator](): AsyncIterator<ChatCompletionChunk, void, unknown> {\n return parseSseJson<ChatCompletionChunk>(this.stream);\n }\n\n async toText(): Promise<string> {\n let full = '';\n for await (const chunk of this) {\n const delta = chunk.choices?.[0]?.delta?.content;\n if (typeof delta === 'string') {\n full += delta;\n }\n }\n return full;\n }\n}\n\n/**\n * Pull SDK-level transport options (timeout, signal, idempotency key) out of\n * the params object so they don't get JSON-serialized into the request body.\n * Returns the request options AND the cleaned-up body.\n */\nfunction splitTransportOptions(params: ChatCompletionCreateParams): {\n body: Record<string, unknown>;\n request: RequestOptions;\n} {\n const { timeoutMs, signal, idempotencyKey, ...rest } = params;\n const headers: Record<string, string> = {};\n if (idempotencyKey) headers['Idempotency-Key'] = idempotencyKey;\n return {\n body: rest as unknown as Record<string, unknown>,\n request: {\n timeoutMs,\n signal,\n ...(Object.keys(headers).length > 0 ? { headers } : {}),\n },\n };\n}\n\nexport class ChatCompletionsApi {\n private readonly http: HttpClient;\n\n constructor(http: HttpClient) {\n this.http = http;\n }\n\n async create(params: ChatCompletionCreateParams & { stream: true }): Promise<ChatCompletionStream>;\n async create(params: ChatCompletionCreateParams & { stream?: false | undefined }): Promise<ChatCompletionResponse>;\n async create(\n params: ChatCompletionCreateParams,\n ): Promise<ChatCompletionResponse | ChatCompletionStream> {\n const { body, request } = splitTransportOptions(params);\n\n if (params.stream) {\n const response = await this.http.postRaw('/llm/v1/chat/completions', body, request);\n if (!response.body) {\n throw new Error('Streaming response body is missing');\n }\n return new ChatCompletionStream(response.body);\n }\n\n return this.http.postJson<ChatCompletionResponse>('/llm/v1/chat/completions', body, request);\n }\n}\n","import type { EmbeddingsCreateParams, EmbeddingsResponse } from '../domain/types';\nimport { HttpClient, type RequestOptions } from '../infrastructure/httpClient';\n\n/**\n * Pull SDK-level transport options out of the params so they don't ride\n * along inside the JSON body. Same shape as ChatCompletionsApi (DRY pattern).\n */\nfunction splitTransportOptions(params: EmbeddingsCreateParams): {\n body: Record<string, unknown>;\n request: RequestOptions;\n} {\n const { timeoutMs, signal, idempotencyKey, ...rest } = params;\n const headers: Record<string, string> = {};\n if (idempotencyKey) headers['Idempotency-Key'] = idempotencyKey;\n return {\n body: rest as unknown as Record<string, unknown>,\n request: {\n timeoutMs,\n signal,\n ...(Object.keys(headers).length > 0 ? { headers } : {}),\n },\n };\n}\n\nexport class EmbeddingsApi {\n private readonly http: HttpClient;\n\n constructor(http: HttpClient) {\n this.http = http;\n }\n\n /**\n * Create one or more text embeddings.\n *\n * NOTE: As of v0.3.0 the gateway accepts the request shape but the underlying\n * vendor wiring is still being rolled out — calls may currently 503 with\n * `code: 'embeddings_not_wired'`. Track gateway PRD §6.7.\n */\n create(params: EmbeddingsCreateParams): Promise<EmbeddingsResponse> {\n const { body, request } = splitTransportOptions(params);\n return this.http.postJson<EmbeddingsResponse>('/llm/v1/embeddings', body, request);\n }\n}\n","import type { ModelsListResponse } from '../domain/types';\nimport { HttpClient } from '../infrastructure/httpClient';\n\nexport class ModelsApi {\n private readonly http: HttpClient;\n\n constructor(http: HttpClient) {\n this.http = http;\n }\n\n list(): Promise<ModelsListResponse> {\n return this.http.getJson<ModelsListResponse>('/llm/v1/models');\n }\n}\n","import type { UsageGetParams, UsageResponse } from '../domain/types';\nimport { HttpClient } from '../infrastructure/httpClient';\n\nexport class UsageApi {\n private readonly http: HttpClient;\n\n constructor(http: HttpClient) {\n this.http = http;\n }\n\n get(params: UsageGetParams = {}): Promise<UsageResponse> {\n const query = typeof params.days === 'number' ? `?days=${encodeURIComponent(String(params.days))}` : '';\n return this.http.getJson<UsageResponse>(`/llm/v1/usage${query}`);\n }\n}\n","export class BuilderforceApiError extends Error {\n public readonly status: number;\n public readonly code?: string;\n public readonly details?: unknown;\n public readonly requestId?: string;\n\n constructor(message: string, status: number, code?: string, details?: unknown, requestId?: string) {\n super(message);\n this.name = 'BuilderforceApiError';\n this.status = status;\n this.code = code;\n this.details = details;\n this.requestId = requestId;\n }\n}\n\nexport interface HttpClientOptions {\n apiKey: string;\n baseUrl: string;\n fetchFn?: typeof fetch;\n /** Default per-request timeout in ms. Overridable per call. */\n timeoutMs?: number;\n}\n\n/** Per-request overrides — passed by the API layer, not by SDK consumers directly. */\nexport interface RequestOptions {\n /** Override the client default timeout for just this request. */\n timeoutMs?: number;\n /** Caller-provided AbortSignal. Linked together with the SDK's internal timeout\n * signal — whichever fires first aborts the request. */\n signal?: AbortSignal;\n /** Extra headers to merge in (e.g. `Idempotency-Key`). */\n headers?: Record<string, string>;\n}\n\nexport class HttpClient {\n private readonly apiKey: string;\n private readonly baseUrl: string;\n private readonly fetchFn: typeof fetch;\n private readonly defaultTimeoutMs: number;\n\n constructor(options: HttpClientOptions) {\n this.apiKey = options.apiKey;\n this.baseUrl = options.baseUrl.replace(/\\/$/, '');\n this.fetchFn = options.fetchFn ?? fetch;\n this.defaultTimeoutMs = options.timeoutMs ?? 60_000;\n }\n\n async getJson<T>(path: string, options?: RequestOptions): Promise<T> {\n const res = await this.fetchWithTimeout(`${this.baseUrl}${path}`, {\n method: 'GET',\n headers: this.mergeHeaders(options),\n }, options);\n return this.parseJsonResponse<T>(res);\n }\n\n async postJson<T>(path: string, body: unknown, options?: RequestOptions): Promise<T> {\n const res = await this.fetchWithTimeout(`${this.baseUrl}${path}`, {\n method: 'POST',\n headers: this.mergeHeaders(options, { 'Content-Type': 'application/json' }),\n body: JSON.stringify(body),\n }, options);\n return this.parseJsonResponse<T>(res);\n }\n\n async postRaw(path: string, body: unknown, options?: RequestOptions): Promise<Response> {\n const res = await this.fetchWithTimeout(`${this.baseUrl}${path}`, {\n method: 'POST',\n headers: this.mergeHeaders(options, { 'Content-Type': 'application/json' }),\n body: JSON.stringify(body),\n }, options);\n if (!res.ok) {\n throw await this.toApiError(res);\n }\n return res;\n }\n\n private mergeHeaders(options?: RequestOptions, base?: Record<string, string>): Record<string, string> {\n return {\n Authorization: `Bearer ${this.apiKey}`,\n ...(base ?? {}),\n ...(options?.headers ?? {}),\n };\n }\n\n /**\n * Wrap a fetch in a combined abort signal: an internal timeout AND any\n * caller-provided signal. Either firing aborts the request. Single source of\n * abort plumbing — every method routes through here (DRY).\n */\n private async fetchWithTimeout(\n input: RequestInfo | URL,\n init: RequestInit,\n options?: RequestOptions,\n ): Promise<Response> {\n const timeoutMs = options?.timeoutMs ?? this.defaultTimeoutMs;\n const timeoutCtl = new AbortController();\n const timer = setTimeout(() => timeoutCtl.abort(), timeoutMs);\n\n // Combine internal timeout signal + caller signal. Native AbortSignal.any\n // (Node 20+ / modern Workers) is preferred; fall back to manual linking.\n const signal = combineSignals(timeoutCtl.signal, options?.signal);\n\n try {\n return await this.fetchFn(input, { ...init, signal });\n } catch (error) {\n if (timeoutCtl.signal.aborted) {\n throw new BuilderforceApiError(`Request timed out after ${timeoutMs}ms`, 408, 'timeout');\n }\n if (options?.signal?.aborted) {\n throw new BuilderforceApiError('Request aborted by caller', 499, 'aborted');\n }\n throw error;\n } finally {\n clearTimeout(timer);\n }\n }\n\n private async parseJsonResponse<T>(res: Response): Promise<T> {\n if (!res.ok) {\n throw await this.toApiError(res);\n }\n return res.json() as Promise<T>;\n }\n\n private async toApiError(res: Response): Promise<BuilderforceApiError> {\n const fallback = `Request failed (${res.status})`;\n const requestId = res.headers.get('x-request-id') ?? undefined;\n try {\n const payload = await res.json() as { error?: string; code?: string; details?: unknown };\n return new BuilderforceApiError(payload.error ?? fallback, res.status, payload.code, payload.details, requestId);\n } catch {\n const text = await res.text().catch(() => '');\n return new BuilderforceApiError(text || fallback, res.status, undefined, undefined, requestId);\n }\n }\n}\n\n/**\n * Combine multiple AbortSignals into one. Uses native `AbortSignal.any` when\n * available (Node 20+, modern Workers); falls back to manual event linking.\n */\nfunction combineSignals(...signals: Array<AbortSignal | undefined>): AbortSignal {\n const live = signals.filter((s): s is AbortSignal => s !== undefined);\n if (live.length === 1) return live[0]!;\n\n const anyImpl = (AbortSignal as unknown as { any?: (signals: AbortSignal[]) => AbortSignal }).any;\n if (typeof anyImpl === 'function') {\n return anyImpl(live);\n }\n\n const ctl = new AbortController();\n for (const s of live) {\n if (s.aborted) { ctl.abort(s.reason); break; }\n s.addEventListener('abort', () => ctl.abort(s.reason), { once: true });\n }\n return ctl.signal;\n}\n","import { ChatCompletionsApi } from './application/ChatCompletionsApi';\nimport { EmbeddingsApi } from './application/EmbeddingsApi';\nimport { ModelsApi } from './application/ModelsApi';\nimport { UsageApi } from './application/UsageApi';\nimport { BuilderforceApiError, HttpClient } from './infrastructure/httpClient';\n\nexport interface BuilderforceClientOptions {\n apiKey: string;\n baseUrl?: string;\n fetch?: typeof fetch;\n /** Default request timeout in ms (default 60_000). Per-call override available\n * via `chat.completions.create({ timeoutMs })` and `embeddings.create({ timeoutMs })`. */\n timeoutMs?: number;\n}\n\nexport class BuilderforceClient {\n public readonly chat: {\n completions: ChatCompletionsApi;\n };\n public readonly embeddings: EmbeddingsApi;\n public readonly models: ModelsApi;\n public readonly usage: UsageApi;\n\n constructor(options: BuilderforceClientOptions) {\n const apiKey = options.apiKey?.trim();\n if (!apiKey) {\n throw new BuilderforceApiError(\n 'BuilderforceClient requires a non-empty apiKey',\n 400,\n 'missing_api_key',\n );\n }\n\n const http = new HttpClient({\n apiKey,\n baseUrl: options.baseUrl ?? 'https://api.builderforce.ai',\n fetchFn: options.fetch,\n timeoutMs: options.timeoutMs,\n });\n\n this.chat = {\n completions: new ChatCompletionsApi(http),\n };\n this.embeddings = new EmbeddingsApi(http);\n this.models = new ModelsApi(http);\n this.usage = new UsageApi(http);\n }\n}\n"],"mappings":"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","names":["splitTransportOptions"]}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@seanhogg/builderforce-sdk",
3
- "version": "0.2.0",
4
- "description": "Typed SDK for the Builderforce.ai LLM gateway — chat completions, models, and usage analytics over an OpenAI-compatible surface.",
3
+ "version": "0.3.0",
4
+ "description": "Typed SDK for the Builderforce.ai LLM gateway — chat completions with tool-calling and structured output, embeddings, models, and usage analytics over an OpenAI-compatible surface.",
5
5
  "license": "MIT",
6
6
  "author": "Sean Hogg",
7
7
  "homepage": "https://github.com/SeanHogg/Builderforce.ai/tree/main/sdk#readme",
@@ -43,11 +43,11 @@
43
43
  "access": "public"
44
44
  },
45
45
  "scripts": {
46
- "check:usecases": "node scripts/check-usecases-sync.mjs",
47
46
  "build": "tsup --config tsup.config.ts",
48
47
  "type-check": "tsc --noEmit",
49
- "test": "npm run check:usecases && vitest run",
50
- "prepublishOnly": "npm run build"
48
+ "test": "vitest run",
49
+ "prepublishOnly": "npm run build",
50
+ "publish:if-new": "node scripts/publish-if-new.mjs"
51
51
  },
52
52
  "engines": {
53
53
  "node": ">=18"