@fastino-ai/pioneer-cli 0.2.10 → 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/src/api.ts DELETED
@@ -1,3187 +0,0 @@
1
- /**
2
- * Felix API client for Pioneer CLI.
3
- * Calls the real /felix/* endpoints.
4
- */
5
-
6
- import { getApiKey, getBaseUrl } from "./config.js";
7
-
8
- export interface ApiResult<T = unknown> {
9
- ok: boolean;
10
- status: number;
11
- data?: T;
12
- error?: string;
13
- }
14
-
15
- /**
16
- * Format authentication error with helpful fix instructions.
17
- */
18
- function formatAuthError(originalError?: string): string {
19
- return [
20
- "Authentication failed. Please login to Pioneer:",
21
- "",
22
- " pioneer auth login",
23
- "",
24
- "Don't have an account? Sign up at https://agent.pioneer.ai/",
25
- originalError ? `\nDetails: ${originalError}` : "",
26
- ]
27
- .filter(Boolean)
28
- .join("\n");
29
- }
30
-
31
- /**
32
- * Check if error is authentication-related.
33
- */
34
- function isAuthError(status: number, errorText: string): boolean {
35
- return (
36
- status === 401 ||
37
- status === 403 ||
38
- errorText.toLowerCase().includes("authentication") ||
39
- errorText.toLowerCase().includes("api key") ||
40
- errorText.toLowerCase().includes("unauthorized")
41
- );
42
- }
43
-
44
- function extractErrorMessage(payload: unknown): string | undefined {
45
- if (!payload) return undefined;
46
- if (typeof payload === "string") return payload;
47
-
48
- if (Array.isArray(payload)) {
49
- const parts = payload
50
- .map((item) => extractErrorMessage(item))
51
- .filter((item): item is string => Boolean(item && item.trim()));
52
- return parts.length > 0 ? parts.join("; ") : undefined;
53
- }
54
-
55
- if (typeof payload === "object") {
56
- const record = payload as Record<string, unknown>;
57
-
58
- if (typeof record.detail === "string") return record.detail;
59
- if (record.detail !== undefined) {
60
- const detailMessage = extractErrorMessage(record.detail);
61
- if (detailMessage) return detailMessage;
62
- }
63
-
64
- if (typeof record.message === "string") return record.message;
65
- if (typeof record.error === "string") return record.error;
66
- if (typeof record.msg === "string") return record.msg;
67
-
68
- if (Array.isArray(record.loc) && typeof record.msg === "string") {
69
- return `${record.loc.join(".")}: ${record.msg}`;
70
- }
71
- }
72
-
73
- return undefined;
74
- }
75
-
76
- async function request<T = unknown>(
77
- method: string,
78
- path: string,
79
- body?: unknown
80
- ): Promise<ApiResult<T>> {
81
- const baseUrl = getBaseUrl().replace(/\/$/, "");
82
- const apiKey = getApiKey();
83
- const url = `${baseUrl}${path}`;
84
-
85
- const headers: Record<string, string> = {
86
- "Content-Type": "application/json",
87
- "User-Agent": "pioneer-cli/0.1.0",
88
- };
89
-
90
- if (apiKey) {
91
- headers["X-API-Key"] = apiKey;
92
- }
93
-
94
- try {
95
- const res = await fetch(url, {
96
- method,
97
- headers,
98
- body: body ? JSON.stringify(body) : undefined,
99
- });
100
-
101
- const text = await res.text();
102
- let data: T | undefined;
103
- try {
104
- data = JSON.parse(text) as T;
105
- } catch {
106
- // Not JSON
107
- }
108
-
109
- if (!res.ok) {
110
- const rawError = data ? JSON.stringify(data) : text || `HTTP ${res.status}`;
111
- const extractedError = extractErrorMessage(data) || text || `HTTP ${res.status}`;
112
-
113
- // Format authentication errors with helpful instructions
114
- if (isAuthError(res.status, rawError)) {
115
- return {
116
- ok: false,
117
- status: res.status,
118
- error: formatAuthError(extractedError),
119
- };
120
- }
121
-
122
- return {
123
- ok: false,
124
- status: res.status,
125
- error: extractedError,
126
- };
127
- }
128
-
129
- return { ok: true, status: res.status, data };
130
- } catch (err) {
131
- return {
132
- ok: false,
133
- status: 0,
134
- error: err instanceof Error ? err.message : String(err),
135
- };
136
- }
137
- }
138
-
139
- // ─────────────────────────────────────────────────────────────────────────────
140
- // Common Types
141
- // ─────────────────────────────────────────────────────────────────────────────
142
-
143
- export interface DatasetRef {
144
- name: string;
145
- version: string;
146
- }
147
-
148
- // ─────────────────────────────────────────────────────────────────────────────
149
- // Auth - validate API key by calling /felix/datasets
150
- // ─────────────────────────────────────────────────────────────────────────────
151
-
152
- export async function validateApiKey(apiKey: string): Promise<ApiResult> {
153
- const baseUrl = getBaseUrl().replace(/\/$/, "");
154
- const url = `${baseUrl}/felix/datasets`;
155
-
156
- try {
157
- const res = await fetch(url, {
158
- method: "GET",
159
- headers: {
160
- "Content-Type": "application/json",
161
- "User-Agent": "pioneer-cli/0.1.0",
162
- "X-API-Key": apiKey,
163
- },
164
- });
165
-
166
- if (res.ok) {
167
- return { ok: true, status: res.status };
168
- }
169
-
170
- if (res.status === 401 || res.status === 403) {
171
- return { ok: false, status: res.status, error: "Invalid API key" };
172
- }
173
-
174
- const text = await res.text();
175
- return { ok: false, status: res.status, error: text || `HTTP ${res.status}` };
176
- } catch (err) {
177
- return {
178
- ok: false,
179
- status: 0,
180
- error: err instanceof Error ? err.message : String(err),
181
- };
182
- }
183
- }
184
-
185
- // ─────────────────────────────────────────────────────────────────────────────
186
- // Datasets
187
- // ─────────────────────────────────────────────────────────────────────────────
188
-
189
- export interface Dataset {
190
- id: string;
191
- user_id: string;
192
- dataset_name: string;
193
- dataset_path: string;
194
- dataset_type: string;
195
- size?: number;
196
- sample_size?: number;
197
- train_ratio?: number;
198
- version_number?: string;
199
- root_dataset_id?: string;
200
- project_id?: string;
201
- schema?: Record<string, unknown>;
202
- schema_warnings?: string[];
203
- validation?: Record<string, unknown>;
204
- annotation_status?: "none" | "in_progress" | "completed";
205
- annotation_config?: Record<string, unknown>;
206
- annotation_progress?: Record<string, unknown>;
207
- status?: "initialized" | "uploading" | "converting" | "validating" | "ready" | "failed" | "generating" | "queued";
208
- processing_error?: string;
209
- type?: "training" | "evaluation" | "benchmark";
210
- visibility?: "private" | "public";
211
- is_competition?: boolean;
212
- labels?: string[];
213
- generation_type?: string;
214
- is_seed?: boolean;
215
- synthesis_session_id?: string;
216
- column_mapping?: Record<string, string>;
217
- created_at: string;
218
- updated_at?: string;
219
- }
220
-
221
- export interface DatasetListResponse {
222
- success: boolean;
223
- datasets: Dataset[];
224
- count: number;
225
- }
226
-
227
- export async function listDatasets(
228
- options: { includeAllVersions?: boolean } = {}
229
- ): Promise<ApiResult<DatasetListResponse>> {
230
- const params = new URLSearchParams();
231
- if (options.includeAllVersions) {
232
- params.set("include_all_versions", "true");
233
- }
234
- const query = params.toString();
235
- const url = query ? `/felix/datasets?${query}` : "/felix/datasets";
236
- return request<DatasetListResponse>("GET", url);
237
- }
238
-
239
- export async function getDataset(dataset: DatasetRef): Promise<ApiResult<Dataset>> {
240
- return request<Dataset>("GET", `/felix/datasets/${dataset.name}/${dataset.version}`);
241
- }
242
-
243
- export interface UploadUrlRequest {
244
- dataset_name: string;
245
- dataset_type?: "classification" | "ner" | "custom" | "decoder";
246
- format?: "jsonl" | "csv" | "parquet";
247
- content_type?: string;
248
- }
249
-
250
- export interface UploadUrlResponse {
251
- upload_url?: string;
252
- presigned_url?: string;
253
- dataset_id: string;
254
- key: string;
255
- expires_in: number;
256
- }
257
-
258
- export async function getUploadUrl(
259
- req: UploadUrlRequest
260
- ): Promise<ApiResult<UploadUrlResponse>> {
261
- return request<UploadUrlResponse>("POST", "/felix/datasets/upload/url", req);
262
- }
263
-
264
- export interface ProcessUploadRequest {
265
- dataset_id: string;
266
- dataset_name: string;
267
- dataset_type?: "classification" | "ner" | "custom" | "decoder";
268
- format?: "jsonl" | "csv" | "parquet";
269
- schema?: Record<string, string>;
270
- project_id?: string;
271
- }
272
-
273
- export async function processUpload(
274
- req: ProcessUploadRequest
275
- ): Promise<ApiResult<Dataset>> {
276
- return request<Dataset>("POST", "/felix/datasets/upload/process", req);
277
- }
278
-
279
- export async function deleteDataset(dataset: DatasetRef): Promise<ApiResult> {
280
- return request("DELETE", `/felix/datasets/${dataset.name}/${dataset.version}`);
281
- }
282
-
283
- export async function deleteAllDatasetVersions(name: string): Promise<ApiResult> {
284
- return request("DELETE", `/felix/datasets/${name}`);
285
- }
286
-
287
- export interface DatasetVersionsResponse {
288
- success: boolean;
289
- versions: Dataset[];
290
- count: number;
291
- }
292
-
293
- export async function listDatasetVersions(
294
- name: string
295
- ): Promise<ApiResult<DatasetVersionsResponse>> {
296
- return request<DatasetVersionsResponse>("GET", `/felix/datasets/${name}`);
297
- }
298
-
299
- export interface DatasetMetadataUpdate {
300
- dataset_name?: string;
301
- dataset_type?: "ner" | "classification" | "custom" | "decoder";
302
- project_id?: string;
303
- }
304
-
305
- export async function updateDatasetMetadata(
306
- dataset: DatasetRef,
307
- update: DatasetMetadataUpdate
308
- ): Promise<ApiResult<Dataset>> {
309
- return request<Dataset>(
310
- "PUT",
311
- `/felix/datasets/${dataset.name}/${dataset.version}`,
312
- update
313
- );
314
- }
315
-
316
- export async function previewDataset(
317
- dataset: DatasetRef,
318
- options: { limit?: number; offset?: number } = {}
319
- ): Promise<ApiResult<Record<string, unknown>>> {
320
- const params = new URLSearchParams();
321
- if (options.limit) params.set("limit", String(options.limit));
322
- if (options.offset) params.set("offset", String(options.offset));
323
- const query = params.toString();
324
- const url = `/felix/datasets/${dataset.name}/${dataset.version}/preview${query ? `?${query}` : ""}`;
325
- return request<Record<string, unknown>>("GET", url);
326
- }
327
-
328
- export interface DatasetUploadRequest {
329
- dataset_name: string;
330
- dataset_type?: "ner" | "classification" | "custom" | "decoder";
331
- format?: "jsonl" | "csv" | "parquet";
332
- schema?: Record<string, string>;
333
- project_id?: string;
334
- }
335
-
336
- export async function uploadDataset(
337
- filePath: string,
338
- options: DatasetUploadRequest
339
- ): Promise<ApiResult<Dataset>> {
340
- try {
341
- const fs = await import("fs");
342
- const path = await import("path");
343
- const fileContent = fs.readFileSync(filePath);
344
- const filename = path.basename(filePath);
345
-
346
- // Detect format from file extension if not provided
347
- const ext = path.extname(filename).slice(1) as "jsonl" | "csv" | "parquet";
348
- const format = options.format || ext || "jsonl";
349
-
350
- // Step 1: Get presigned upload URL
351
- const urlResult = await getUploadUrl({
352
- dataset_name: options.dataset_name,
353
- dataset_type: options.dataset_type,
354
- format,
355
- content_type: "application/octet-stream",
356
- });
357
-
358
- if (!urlResult.ok || !urlResult.data) {
359
- return {
360
- ok: false,
361
- status: urlResult.status,
362
- error: urlResult.error || "Failed to get upload URL",
363
- };
364
- }
365
-
366
- const { dataset_id } = urlResult.data;
367
- const uploadUrl = urlResult.data.upload_url || urlResult.data.presigned_url;
368
- if (!uploadUrl) {
369
- return {
370
- ok: false,
371
- status: 500,
372
- error: "Upload URL not provided by backend",
373
- };
374
- }
375
-
376
- // Step 2: Upload file directly to S3 via presigned URL
377
- const uploadRes = await fetch(uploadUrl, {
378
- method: "PUT",
379
- headers: {
380
- "Content-Type": "application/octet-stream",
381
- },
382
- body: fileContent,
383
- });
384
-
385
- if (!uploadRes.ok) {
386
- return {
387
- ok: false,
388
- status: uploadRes.status,
389
- error: `Failed to upload file to S3: HTTP ${uploadRes.status}`,
390
- };
391
- }
392
-
393
- // Step 3: Trigger processing
394
- const processResult = await processUpload({
395
- dataset_id,
396
- dataset_name: options.dataset_name,
397
- dataset_type: options.dataset_type,
398
- format,
399
- schema: options.schema,
400
- project_id: options.project_id,
401
- });
402
-
403
- return processResult;
404
- } catch (err) {
405
- return {
406
- ok: false,
407
- status: 0,
408
- error: err instanceof Error ? err.message : String(err),
409
- };
410
- }
411
- }
412
-
413
- export interface DatasetDownloadResponse {
414
- download_url: string;
415
- file_path?: string;
416
- size?: number;
417
- dataset_name?: string;
418
- version?: string;
419
- }
420
-
421
- export async function downloadDataset(
422
- dataset: DatasetRef,
423
- format: "csv" | "jsonl" | "parquet" = "jsonl",
424
- outputPath?: string
425
- ): Promise<ApiResult<DatasetDownloadResponse>> {
426
- const baseUrl = getBaseUrl().replace(/\/$/, "");
427
- const apiKey = getApiKey();
428
- const downloadUrl = `${baseUrl}/felix/datasets/${dataset.name}/${dataset.version}/download?format=${format}`;
429
-
430
- // Generate default filename if not specified
431
- const safeDatasetName = dataset.name.replace(/[^a-zA-Z0-9-_]/g, "_");
432
- if (!outputPath) {
433
- outputPath = `${safeDatasetName}_${dataset.version}.${format}`;
434
- }
435
-
436
- // Download the file
437
- const headers: Record<string, string> = {
438
- "User-Agent": "pioneer-cli/0.1.0",
439
- };
440
-
441
- if (apiKey) {
442
- headers["X-API-Key"] = apiKey;
443
- }
444
-
445
- try {
446
- const res = await fetch(downloadUrl, {
447
- method: "GET",
448
- headers,
449
- });
450
-
451
- if (!res.ok) {
452
- const text = await res.text();
453
- return {
454
- ok: false,
455
- status: res.status,
456
- error: text || `HTTP ${res.status}`,
457
- };
458
- }
459
-
460
- // Write the file
461
- const fs = await import("fs");
462
- const path = await import("path");
463
- const buffer = await res.arrayBuffer();
464
- fs.writeFileSync(outputPath, Buffer.from(buffer));
465
-
466
- // Resolve to absolute path for user feedback
467
- const absolutePath = path.resolve(outputPath);
468
-
469
- return {
470
- ok: true,
471
- status: 200,
472
- data: {
473
- download_url: downloadUrl,
474
- file_path: absolutePath,
475
- size: buffer.byteLength,
476
- dataset_name: dataset.name,
477
- version: dataset.version,
478
- },
479
- };
480
- } catch (err) {
481
- return {
482
- ok: false,
483
- status: 0,
484
- error: err instanceof Error ? err.message : String(err),
485
- };
486
- }
487
- }
488
-
489
- export interface DatasetAnalysisRequest {
490
- dataset: DatasetRef;
491
- task_type: string;
492
- analyses: string[];
493
- }
494
-
495
- // Diversity Analysis Types
496
- export interface DiversityPoint {
497
- x?: number;
498
- y?: number;
499
- z?: number;
500
- w?: number;
501
- coordinates: number[];
502
- text?: string;
503
- token_count?: number;
504
- labels?: string[];
505
- metadata?: Record<string, unknown>;
506
- sample_index?: number;
507
- similarity_to_centroid?: number;
508
- embedding?: number[];
509
- }
510
-
511
- export interface DiversityVisualization {
512
- method: "pca" | "tsne";
513
- dimensions: number;
514
- points: DiversityPoint[];
515
- tsne_perplexity?: number;
516
- similarity_range?: { min: number; max: number };
517
- token_count_range?: { min: number; max: number };
518
- }
519
-
520
- export interface DiversityLLMAnalysis {
521
- reasoning_trace: string;
522
- summary: string;
523
- diversity_rating: "low" | "moderate" | "high" | "excellent";
524
- key_observations: string[];
525
- recommendations: string[];
526
- model_used: string;
527
- }
528
-
529
- export interface DiversityAnalysis {
530
- vendi_score: number;
531
- sample_size: number;
532
- interpretation?: string;
533
- visualization?: DiversityVisualization;
534
- llm_analysis?: DiversityLLMAnalysis;
535
- }
536
-
537
- // Distribution Analysis Types
538
- export interface DistributionAnalysis {
539
- label_counts: Record<string, number>;
540
- total_samples: number;
541
- unique_labels: number;
542
- most_common_label?: string;
543
- least_common_label?: string;
544
- imbalance_ratio?: number;
545
- }
546
-
547
- // Duplicates Analysis Types
548
- export interface DuplicatesAnalysis {
549
- total_duplicates: number;
550
- duplicate_groups: number;
551
- duplicate_percentage: number;
552
- examples?: Array<{ text: string; count: number }>;
553
- }
554
-
555
- // Outliers Analysis Types
556
- export interface OutliersAnalysis {
557
- total_outliers: number;
558
- outlier_percentage: number;
559
- method: string;
560
- threshold?: number;
561
- examples?: Array<{ text: string; score: number; reason?: string }>;
562
- }
563
-
564
- // Splits Analysis Types
565
- export interface SplitsAnalysis {
566
- train_size: number;
567
- validation_size: number;
568
- test_size?: number;
569
- train_percentage: number;
570
- validation_percentage: number;
571
- test_percentage?: number;
572
- }
573
-
574
- export interface DatasetAnalysisResponse {
575
- summary: Record<string, unknown>;
576
- distribution?: DistributionAnalysis;
577
- duplicates?: DuplicatesAnalysis;
578
- outliers?: OutliersAnalysis;
579
- correlations?: Record<string, unknown>;
580
- splits?: SplitsAnalysis;
581
- errors?: Record<string, unknown>;
582
- diversity?: DiversityAnalysis;
583
- natural_language_response?: string;
584
- }
585
-
586
- export async function analyzeDataset(
587
- dataset: DatasetRef,
588
- taskType: string,
589
- analyses: string[]
590
- ): Promise<ApiResult<DatasetAnalysisResponse>> {
591
- return request<DatasetAnalysisResponse>("POST", "/felix/dataset/analyze", {
592
- dataset_name: dataset.name,
593
- dataset_version: dataset.version,
594
- task_type: taskType,
595
- analyses: analyses,
596
- });
597
- }
598
-
599
- export interface DatasetLLMAnalysisRequest {
600
- task_type: "ner" | "classification" | "generative";
601
- task_description?: string;
602
- labels?: string[];
603
- dataset?: Record<string, unknown>[];
604
- dataset_name?: string;
605
- dataset_version?: string;
606
- }
607
-
608
- export async function analyzeDatasetLLM(
609
- req: DatasetLLMAnalysisRequest
610
- ): Promise<ApiResult<DiversityLLMAnalysis>> {
611
- return request<DiversityLLMAnalysis>("POST", "/felix/dataset/analyze_llm", req);
612
- }
613
-
614
- // ─────────────────────────────────────────────────────────────────────────────
615
- // Dataset Generation
616
- // ─────────────────────────────────────────────────────────────────────────────
617
-
618
- export interface RecordField {
619
- name: string;
620
- type?: string;
621
- description?: string;
622
- allowed_values?: string[];
623
- }
624
-
625
- export interface ConstraintRequest {
626
- description: string;
627
- choices?: string[];
628
- probability?: number;
629
- }
630
-
631
- export interface GenerateResponse {
632
- success: boolean;
633
- data: Record<string, unknown>[];
634
- count: number;
635
- task_type: string;
636
- token_usage?: number;
637
- dataset?: Record<string, unknown>;
638
- }
639
-
640
- export type GenerateTaskType =
641
- | "ner"
642
- | "classification"
643
- | "custom"
644
- | "decoder"
645
- | "records"
646
- | "fields";
647
-
648
- export interface GenerateAsyncResponse {
649
- job_id: string;
650
- status?: string;
651
- dataset_name: string;
652
- task_type: GenerateTaskType;
653
- is_seed?: boolean;
654
- message?: string;
655
- }
656
-
657
- export interface GenerateJobStatus {
658
- job_id: string;
659
- status: "queued" | "generating" | "ready" | "failed" | string;
660
- data?: Record<string, unknown>[];
661
- count?: number;
662
- task_type?: string;
663
- token_usage?: number;
664
- dataset?: Record<string, unknown>;
665
- error?: string;
666
- is_seed?: boolean;
667
- created_at?: string;
668
- }
669
-
670
- export interface GenerateRequest {
671
- task_type: GenerateTaskType;
672
- dataset_name: string;
673
- num_examples?: number;
674
- domain_description?: string;
675
- temperature?: number;
676
- quality?: "light" | "medium" | "heavy";
677
- generation_profile?: "auto" | "fast" | "balanced" | "quality";
678
- session_id?: string;
679
- config_num_examples?: number;
680
- seed?: number;
681
- labels?: string[];
682
- classified_examples?: Record<string, unknown>[];
683
- multi_label?: boolean;
684
- class_balance?: Record<string, number>;
685
- batch_size?: number;
686
- negative_ratio?: number;
687
- fields?: RecordField[];
688
- input_fields?: RecordField[];
689
- output_fields?: RecordField[];
690
- prompt?: string;
691
- output_format?: Record<string, unknown>;
692
- infer_output_format?: boolean;
693
- instruction?: string;
694
- constraints?: ConstraintRequest[];
695
- multiplicator?: Record<string, unknown>;
696
- include_reasoning_trace?: boolean;
697
- reasoning_effort?: "low" | "medium" | "high";
698
- use_meta_felix?: boolean;
699
- min_criteria?: number;
700
- target_choices?: number;
701
- project_id?: string;
702
- type?: "training" | "evaluation" | "split";
703
- visibility?: "private" | "public";
704
- split_ratio?: Record<string, number>;
705
- }
706
-
707
- const GENERATION_POLL_INTERVAL_MS = 2000;
708
- const GENERATION_MAX_ATTEMPTS = 300; // 10 minutes
709
-
710
- function sleep(ms: number): Promise<void> {
711
- return new Promise((resolve) => setTimeout(resolve, ms));
712
- }
713
-
714
- function makeDefaultDatasetName(taskType: GenerateTaskType): string {
715
- return `${taskType}-dataset-${Date.now()}`;
716
- }
717
-
718
- export async function startGeneration(
719
- req: GenerateRequest,
720
- options: { isSeed?: boolean; synthesisSessionId?: string } = {}
721
- ): Promise<ApiResult<GenerateAsyncResponse>> {
722
- const params = new URLSearchParams();
723
- if (options.isSeed) params.set("is_seed", "true");
724
- if (options.synthesisSessionId) {
725
- params.set("synthesis_session_id", options.synthesisSessionId);
726
- }
727
- const query = params.toString();
728
- const url = query ? `/generate?${query}` : "/generate";
729
- return request<GenerateAsyncResponse>("POST", url, req);
730
- }
731
-
732
- export async function getGenerationJobStatus(
733
- jobId: string
734
- ): Promise<ApiResult<GenerateJobStatus>> {
735
- return request<GenerateJobStatus>("GET", `/generate/jobs/${jobId}`);
736
- }
737
-
738
- export async function waitForGenerationJob(
739
- jobId: string,
740
- options: { pollIntervalMs?: number; maxAttempts?: number } = {}
741
- ): Promise<ApiResult<GenerateJobStatus>> {
742
- const pollIntervalMs = options.pollIntervalMs ?? GENERATION_POLL_INTERVAL_MS;
743
- const maxAttempts = options.maxAttempts ?? GENERATION_MAX_ATTEMPTS;
744
-
745
- for (let attempt = 0; attempt < maxAttempts; attempt++) {
746
- const statusResult = await getGenerationJobStatus(jobId);
747
- if (!statusResult.ok || !statusResult.data) {
748
- if (statusResult.status === 404 && attempt < 5) {
749
- await sleep(pollIntervalMs);
750
- continue;
751
- }
752
- return {
753
- ok: false,
754
- status: statusResult.status,
755
- error: statusResult.error ?? "Failed to fetch generation status",
756
- };
757
- }
758
-
759
- if (statusResult.data.status === "ready") {
760
- return statusResult;
761
- }
762
-
763
- if (statusResult.data.status === "failed") {
764
- return {
765
- ok: false,
766
- status: statusResult.status || 500,
767
- error: statusResult.data.error ?? "Generation failed",
768
- };
769
- }
770
-
771
- await sleep(pollIntervalMs);
772
- }
773
-
774
- return {
775
- ok: false,
776
- status: 408,
777
- error: `Generation timed out for job ${jobId}. Use 'pioneer dataset get <name[:version]>' once ready.`,
778
- };
779
- }
780
-
781
- async function generateDataset(req: GenerateRequest): Promise<ApiResult<GenerateJobStatus>> {
782
- const startResult = await startGeneration(req);
783
- if (!startResult.ok || !startResult.data) {
784
- return {
785
- ok: false,
786
- status: startResult.status,
787
- error: startResult.error ?? "Failed to start generation job",
788
- };
789
- }
790
-
791
- return waitForGenerationJob(startResult.data.job_id);
792
- }
793
-
794
- export interface InferLabelsResponse {
795
- success: boolean;
796
- labels: string[];
797
- count: number;
798
- }
799
-
800
- export interface InferFieldsResponse {
801
- success: boolean;
802
- input_fields: RecordField[];
803
- output_fields: RecordField[];
804
- mode: string;
805
- reasoning: string;
806
- }
807
-
808
- // NER Generation
809
- export interface GenerateNERRequest {
810
- labels: string[];
811
- num_examples?: number;
812
- domain_description?: string;
813
- seed?: number;
814
- session_id?: string;
815
- config_num_examples?: number;
816
- temperature?: number;
817
- constraints?: ConstraintRequest[];
818
- save_dataset?: boolean;
819
- dataset_name?: string;
820
- project_id?: string;
821
- generation_profile?: "auto" | "fast" | "balanced" | "quality";
822
- include_reasoning_trace?: boolean;
823
- reasoning_effort?: "low" | "medium" | "high";
824
- quality?: "light" | "medium" | "heavy";
825
- type?: "training" | "evaluation" | "split";
826
- visibility?: "private" | "public";
827
- negative_ratio?: number;
828
- split_ratio?: Record<string, number>;
829
- multiplicator?: Record<string, unknown>;
830
- use_meta_felix?: boolean;
831
- min_criteria?: number;
832
- target_choices?: number;
833
- classified_examples?: Record<string, unknown>[];
834
- }
835
-
836
- export async function generateNER(
837
- req: GenerateNERRequest
838
- ): Promise<ApiResult<GenerateJobStatus>> {
839
- return generateDataset({
840
- task_type: "ner",
841
- dataset_name: req.dataset_name ?? makeDefaultDatasetName("ner"),
842
- labels: req.labels,
843
- num_examples: req.num_examples,
844
- domain_description: req.domain_description,
845
- seed: req.seed,
846
- session_id: req.session_id,
847
- config_num_examples: req.config_num_examples,
848
- temperature: req.temperature,
849
- constraints: req.constraints,
850
- project_id: req.project_id,
851
- generation_profile: req.generation_profile,
852
- include_reasoning_trace: req.include_reasoning_trace,
853
- reasoning_effort: req.reasoning_effort,
854
- quality: req.quality,
855
- min_criteria: req.min_criteria,
856
- target_choices: req.target_choices,
857
- type: req.type,
858
- visibility: req.visibility,
859
- negative_ratio: req.negative_ratio,
860
- split_ratio: req.split_ratio,
861
- multiplicator: req.multiplicator,
862
- use_meta_felix: req.use_meta_felix,
863
- classified_examples: req.classified_examples,
864
- });
865
- }
866
-
867
- export interface LabelExistingNERRequest {
868
- labels: string[];
869
- inputs: string[];
870
- domain_description?: string;
871
- seed?: number;
872
- temperature?: number;
873
- constraints?: ConstraintRequest[];
874
- save_dataset?: boolean;
875
- dataset_name?: string;
876
- project_id?: string;
877
- }
878
-
879
- export async function labelExistingNER(
880
- req: LabelExistingNERRequest
881
- ): Promise<ApiResult<GenerateResponse>> {
882
- return request<GenerateResponse>("POST", "/generate/ner/label-existing", req);
883
- }
884
-
885
- export interface InferNERLabelsRequest {
886
- domain_description: string;
887
- }
888
-
889
- export async function inferNERLabels(
890
- req: InferNERLabelsRequest
891
- ): Promise<ApiResult<InferLabelsResponse>> {
892
- return request<InferLabelsResponse>("POST", "/generate/ner/infer-labels", req);
893
- }
894
-
895
- // Classification Generation
896
- export interface GenerateClassificationRequest {
897
- labels: string[];
898
- num_examples?: number;
899
- domain_description?: string;
900
- seed?: number;
901
- class_balance?: Record<string, number>;
902
- session_id?: string;
903
- config_num_examples?: number;
904
- temperature?: number;
905
- batch_size?: number;
906
- constraints?: ConstraintRequest[];
907
- multi_label?: boolean;
908
- save_dataset?: boolean;
909
- dataset_name?: string;
910
- project_id?: string;
911
- generation_profile?: "auto" | "fast" | "balanced" | "quality";
912
- include_reasoning_trace?: boolean;
913
- reasoning_effort?: "low" | "medium" | "high";
914
- quality?: "light" | "medium" | "heavy";
915
- min_criteria?: number;
916
- target_choices?: number;
917
- split_ratio?: Record<string, number>;
918
- negative_ratio?: number;
919
- multiplicator?: Record<string, unknown>;
920
- use_meta_felix?: boolean;
921
- type?: "training" | "evaluation" | "split";
922
- visibility?: "private" | "public";
923
- }
924
-
925
- export async function generateClassification(
926
- req: GenerateClassificationRequest
927
- ): Promise<ApiResult<GenerateJobStatus>> {
928
- return generateDataset({
929
- task_type: "classification",
930
- dataset_name: req.dataset_name ?? makeDefaultDatasetName("classification"),
931
- labels: req.labels,
932
- num_examples: req.num_examples,
933
- domain_description: req.domain_description,
934
- seed: req.seed,
935
- class_balance: req.class_balance,
936
- session_id: req.session_id,
937
- config_num_examples: req.config_num_examples,
938
- temperature: req.temperature,
939
- batch_size: req.batch_size,
940
- constraints: req.constraints,
941
- multi_label: req.multi_label,
942
- project_id: req.project_id,
943
- generation_profile: req.generation_profile,
944
- include_reasoning_trace: req.include_reasoning_trace,
945
- reasoning_effort: req.reasoning_effort,
946
- quality: req.quality,
947
- min_criteria: req.min_criteria,
948
- target_choices: req.target_choices,
949
- split_ratio: req.split_ratio,
950
- multiplicator: req.multiplicator,
951
- use_meta_felix: req.use_meta_felix,
952
- type: req.type,
953
- visibility: req.visibility,
954
- negative_ratio: req.negative_ratio,
955
- });
956
- }
957
-
958
- export interface LabelExistingClassificationRequest {
959
- labels: string[];
960
- inputs: string[];
961
- domain_description?: string;
962
- seed?: number;
963
- class_balance?: Record<string, number>;
964
- temperature?: number;
965
- batch_size?: number;
966
- constraints?: ConstraintRequest[];
967
- multi_label?: boolean;
968
- save_dataset?: boolean;
969
- dataset_name?: string;
970
- project_id?: string;
971
- }
972
-
973
- export async function labelExistingClassification(
974
- req: LabelExistingClassificationRequest
975
- ): Promise<ApiResult<GenerateResponse>> {
976
- return request<GenerateResponse>("POST", "/generate/classification/label-existing", req);
977
- }
978
-
979
- export interface InferClassificationLabelsRequest {
980
- domain_description: string;
981
- }
982
-
983
- export async function inferClassificationLabels(
984
- req: InferClassificationLabelsRequest
985
- ): Promise<ApiResult<InferLabelsResponse>> {
986
- return request<InferLabelsResponse>("POST", "/generate/classification/infer-labels", req);
987
- }
988
-
989
- // Custom Generation
990
- export interface GenerateCustomRequest {
991
- prompt: string;
992
- output_format?: Record<string, unknown>;
993
- infer_output_format?: boolean;
994
- num_examples?: number;
995
- seed?: number;
996
- session_id?: string;
997
- temperature?: number;
998
- constraints?: ConstraintRequest[];
999
- save_dataset?: boolean;
1000
- dataset_name?: string;
1001
- project_id?: string;
1002
- generation_profile?: "auto" | "fast" | "balanced" | "quality";
1003
- include_reasoning_trace?: boolean;
1004
- reasoning_effort?: "low" | "medium" | "high";
1005
- quality?: "light" | "medium" | "heavy";
1006
- min_criteria?: number;
1007
- target_choices?: number;
1008
- split_ratio?: Record<string, number>;
1009
- negative_ratio?: number;
1010
- multiplicator?: Record<string, unknown>;
1011
- use_meta_felix?: boolean;
1012
- type?: "training" | "evaluation" | "split";
1013
- visibility?: "private" | "public";
1014
- }
1015
-
1016
- export async function generateCustom(
1017
- req: GenerateCustomRequest
1018
- ): Promise<ApiResult<GenerateJobStatus>> {
1019
- return generateDataset({
1020
- task_type: "custom",
1021
- dataset_name: req.dataset_name ?? makeDefaultDatasetName("custom"),
1022
- prompt: req.prompt,
1023
- output_format: req.output_format,
1024
- infer_output_format: req.infer_output_format,
1025
- num_examples: req.num_examples,
1026
- seed: req.seed,
1027
- session_id: req.session_id,
1028
- min_criteria: req.min_criteria,
1029
- temperature: req.temperature,
1030
- constraints: req.constraints,
1031
- project_id: req.project_id,
1032
- generation_profile: req.generation_profile,
1033
- include_reasoning_trace: req.include_reasoning_trace,
1034
- reasoning_effort: req.reasoning_effort,
1035
- target_choices: req.target_choices,
1036
- quality: req.quality,
1037
- split_ratio: req.split_ratio,
1038
- negative_ratio: req.negative_ratio,
1039
- multiplicator: req.multiplicator,
1040
- use_meta_felix: req.use_meta_felix,
1041
- type: req.type,
1042
- visibility: req.visibility,
1043
- });
1044
- }
1045
-
1046
- // Records Generation
1047
- export interface GenerateRecordsRequest {
1048
- fields: RecordField[];
1049
- dataset_name?: string;
1050
- num_examples?: number;
1051
- domain_description?: string;
1052
- seed?: number;
1053
- temperature?: number;
1054
- constraints?: ConstraintRequest[];
1055
- quality?: "light" | "medium" | "heavy";
1056
- generation_profile?: "auto" | "fast" | "balanced" | "quality";
1057
- include_reasoning_trace?: boolean;
1058
- reasoning_effort?: "low" | "medium" | "high";
1059
- min_criteria?: number;
1060
- target_choices?: number;
1061
- split_ratio?: Record<string, number>;
1062
- negative_ratio?: number;
1063
- multiplicator?: Record<string, unknown>;
1064
- project_id?: string;
1065
- use_meta_felix?: boolean;
1066
- type?: "training" | "evaluation" | "split";
1067
- visibility?: "private" | "public";
1068
- classified_examples?: Record<string, unknown>[];
1069
- }
1070
-
1071
- export async function generateRecords(
1072
- req: GenerateRecordsRequest
1073
- ): Promise<ApiResult<GenerateJobStatus>> {
1074
- return generateDataset({
1075
- task_type: "records",
1076
- dataset_name: req.dataset_name ?? makeDefaultDatasetName("records"),
1077
- fields: req.fields,
1078
- num_examples: req.num_examples,
1079
- domain_description: req.domain_description,
1080
- seed: req.seed,
1081
- temperature: req.temperature,
1082
- constraints: req.constraints,
1083
- project_id: req.project_id,
1084
- generation_profile: req.generation_profile,
1085
- include_reasoning_trace: req.include_reasoning_trace,
1086
- reasoning_effort: req.reasoning_effort,
1087
- quality: req.quality,
1088
- min_criteria: req.min_criteria,
1089
- target_choices: req.target_choices,
1090
- split_ratio: req.split_ratio,
1091
- negative_ratio: req.negative_ratio,
1092
- multiplicator: req.multiplicator,
1093
- use_meta_felix: req.use_meta_felix,
1094
- type: req.type,
1095
- visibility: req.visibility,
1096
- classified_examples: req.classified_examples,
1097
- });
1098
- }
1099
-
1100
- // Fields Generation
1101
- export interface GenerateFieldsRequest {
1102
- input_fields: RecordField[];
1103
- output_fields: RecordField[];
1104
- dataset_name?: string;
1105
- num_examples?: number;
1106
- domain_description?: string;
1107
- seed?: number;
1108
- temperature?: number;
1109
- constraints?: ConstraintRequest[];
1110
- save_dataset?: boolean;
1111
- quality?: "light" | "medium" | "heavy";
1112
- generation_profile?: "auto" | "fast" | "balanced" | "quality";
1113
- include_reasoning_trace?: boolean;
1114
- reasoning_effort?: "low" | "medium" | "high";
1115
- min_criteria?: number;
1116
- target_choices?: number;
1117
- split_ratio?: Record<string, number>;
1118
- negative_ratio?: number;
1119
- multiplicator?: Record<string, unknown>;
1120
- project_id?: string;
1121
- use_meta_felix?: boolean;
1122
- type?: "training" | "evaluation" | "split";
1123
- visibility?: "private" | "public";
1124
- classified_examples?: Record<string, unknown>[];
1125
- }
1126
-
1127
- export async function generateFields(
1128
- req: GenerateFieldsRequest
1129
- ): Promise<ApiResult<GenerateJobStatus>> {
1130
- return generateDataset({
1131
- task_type: "fields",
1132
- dataset_name: req.dataset_name ?? makeDefaultDatasetName("fields"),
1133
- input_fields: req.input_fields,
1134
- output_fields: req.output_fields,
1135
- num_examples: req.num_examples,
1136
- domain_description: req.domain_description,
1137
- seed: req.seed,
1138
- temperature: req.temperature,
1139
- constraints: req.constraints,
1140
- project_id: req.project_id,
1141
- generation_profile: req.generation_profile,
1142
- include_reasoning_trace: req.include_reasoning_trace,
1143
- reasoning_effort: req.reasoning_effort,
1144
- quality: req.quality,
1145
- min_criteria: req.min_criteria,
1146
- target_choices: req.target_choices,
1147
- split_ratio: req.split_ratio,
1148
- negative_ratio: req.negative_ratio,
1149
- multiplicator: req.multiplicator,
1150
- use_meta_felix: req.use_meta_felix,
1151
- type: req.type,
1152
- visibility: req.visibility,
1153
- classified_examples: req.classified_examples,
1154
- });
1155
- }
1156
-
1157
- export interface LabelExistingFieldsRequest {
1158
- input_fields: RecordField[];
1159
- output_fields: RecordField[];
1160
- inputs: Record<string, unknown>[];
1161
- domain_description?: string;
1162
- seed?: number;
1163
- temperature?: number;
1164
- constraints?: ConstraintRequest[];
1165
- save_dataset?: boolean;
1166
- dataset_name?: string;
1167
- project_id?: string;
1168
- }
1169
-
1170
- export async function labelExistingFields(
1171
- req: LabelExistingFieldsRequest
1172
- ): Promise<ApiResult<GenerateResponse>> {
1173
- return request<GenerateResponse>("POST", "/generate/fields/label-existing", req);
1174
- }
1175
-
1176
- export interface InferFieldsRequest {
1177
- domain_description: string;
1178
- }
1179
-
1180
- export async function inferFields(
1181
- req: InferFieldsRequest
1182
- ): Promise<ApiResult<InferFieldsResponse>> {
1183
- return request<InferFieldsResponse>("POST", "/generate/fields/infer-fields", req);
1184
- }
1185
-
1186
- export interface ImprovePromptRequest {
1187
- prompt: string;
1188
- data_type?: string;
1189
- }
1190
-
1191
- export interface ImprovePromptResponse {
1192
- success: boolean;
1193
- improved_prompt: string;
1194
- summary: string;
1195
- }
1196
-
1197
- export async function improvePrompt(
1198
- req: ImprovePromptRequest
1199
- ): Promise<ApiResult<ImprovePromptResponse>> {
1200
- return request<ImprovePromptResponse>("POST", "/generate/improve-prompt", req);
1201
- }
1202
-
1203
- export interface InferAdvancedRequest {
1204
- prompt: string;
1205
- data_type?: string;
1206
- labels?: string[];
1207
- }
1208
-
1209
- export interface InferredConstraint {
1210
- description: string;
1211
- choices?: string[];
1212
- probability?: number;
1213
- }
1214
-
1215
- export interface InferredMultiplicator {
1216
- prompt: string;
1217
- choices: string[];
1218
- }
1219
-
1220
- export interface InferAdvancedResponse {
1221
- success: boolean;
1222
- constraints: InferredConstraint[];
1223
- multiplicator?: InferredMultiplicator;
1224
- }
1225
-
1226
- export async function inferAdvanced(
1227
- req: InferAdvancedRequest
1228
- ): Promise<ApiResult<InferAdvancedResponse>> {
1229
- return request<InferAdvancedResponse>("POST", "/generate/infer-advanced", req);
1230
- }
1231
-
1232
- // ─────────────────────────────────────────────────────────────────────────────
1233
- // Training Jobs
1234
- // ─────────────────────────────────────────────────────────────────────────────
1235
-
1236
- export interface TrainingJob {
1237
- id: string;
1238
- user_id: string;
1239
- project_id?: string;
1240
- model_name?: string;
1241
- datasets: Array<{ name: string; version?: string }>;
1242
- base_model: string;
1243
- validation_data_percentage: number;
1244
- nr_epochs: number;
1245
- learning_rate: number;
1246
- batch_size: number;
1247
- trained_model_path?: string;
1248
- job_reference?: string;
1249
- instance_type?: string;
1250
- status: string;
1251
- error_message?: string;
1252
- created_at: string;
1253
- updated_at: string;
1254
- started_at?: string;
1255
- completed_at?: string;
1256
- model_auto_selected?: boolean;
1257
- model_selection_reason?: string;
1258
- task_type?: string;
1259
- labels?: string[];
1260
- example?: string;
1261
- deployment_status?: string;
1262
- metrics?: Record<string, unknown>;
1263
- version_number?: string;
1264
- root_job_id?: string;
1265
- }
1266
-
1267
- export interface TrainingJobListResponse {
1268
- success: boolean;
1269
- training_jobs: TrainingJob[];
1270
- count: number;
1271
- }
1272
-
1273
- export async function listJobs(
1274
- options: { status?: string; project_id?: string; limit?: number } = {}
1275
- ): Promise<ApiResult<TrainingJobListResponse>> {
1276
- const params = new URLSearchParams();
1277
- if (options.status) params.set("status", options.status);
1278
- if (options.project_id) params.set("project_id", options.project_id);
1279
- if (options.limit !== undefined) params.set("limit", String(options.limit));
1280
- const query = params.toString();
1281
- const url = query ? `/felix/training-jobs?${query}` : "/felix/training-jobs";
1282
- return request<TrainingJobListResponse>("GET", url);
1283
- }
1284
-
1285
- export async function getJob(jobId: string): Promise<ApiResult<TrainingJob>> {
1286
- return request<TrainingJob>("GET", `/felix/training-jobs/${jobId}`);
1287
- }
1288
-
1289
- /**
1290
- * Assign (or unassign) a training job to a project. The deploy endpoint
1291
- * (`POST /projects/{id}/deployments`) requires the job's `project_id` to
1292
- * match the target project, so callers typically PATCH this before deploy.
1293
- *
1294
- * @param jobId Training job UUID.
1295
- * @param projectId Project UUID to attach to, or `null` to unassign.
1296
- */
1297
- export async function updateTrainingJob(
1298
- jobId: string,
1299
- body: { project_id: string | null }
1300
- ): Promise<ApiResult<{ success?: boolean; message?: string }>> {
1301
- return request<{ success?: boolean; message?: string }>(
1302
- "PATCH",
1303
- `/felix/training-jobs/${jobId}`,
1304
- body
1305
- );
1306
- }
1307
-
1308
- export interface TrainingJobCreateRequest {
1309
- model_name: string;
1310
- datasets: DatasetRef[];
1311
- base_model?: string;
1312
- auto_select_model?: boolean;
1313
- validation_data_percentage?: number;
1314
- nr_epochs?: number;
1315
- learning_rate?: number;
1316
- batch_size?: number;
1317
- save_steps?: number;
1318
- wandb_api_key?: string;
1319
- project_id?: string;
1320
- training_type?: "full" | "lora";
1321
- lora_r?: number;
1322
- lora_alpha?: number;
1323
- lora_dropout?: number;
1324
- enable_probe_override?: boolean;
1325
- probe_delta_threshold?: number;
1326
- }
1327
-
1328
- export async function createJob(
1329
- req: TrainingJobCreateRequest
1330
- ): Promise<ApiResult<TrainingJob>> {
1331
- return request<TrainingJob>("POST", "/felix/training-jobs", req);
1332
- }
1333
-
1334
- export interface TrainingLog {
1335
- id: string;
1336
- job_id: string;
1337
- timestamp: string;
1338
- level: string;
1339
- message: string;
1340
- source: string;
1341
- }
1342
-
1343
- export interface TrainingLogsResponse {
1344
- job_id: string;
1345
- logs: TrainingLog[];
1346
- total_logs: number;
1347
- }
1348
-
1349
- export async function getJobLogs(
1350
- jobId: string
1351
- ): Promise<ApiResult<TrainingLogsResponse>> {
1352
- return request<TrainingLogsResponse>("GET", `/felix/training-jobs/${jobId}/logs`);
1353
- }
1354
-
1355
- export async function deleteJob(jobId: string): Promise<ApiResult> {
1356
- return request("DELETE", `/felix/training-jobs/${jobId}`);
1357
- }
1358
-
1359
- export interface StopJobResponse {
1360
- success: boolean;
1361
- message: string;
1362
- job_id: string;
1363
- status: string;
1364
- }
1365
-
1366
- export async function stopJob(
1367
- jobId: string
1368
- ): Promise<ApiResult<StopJobResponse>> {
1369
- return request<StopJobResponse>("POST", `/felix/training-jobs/${jobId}/stop`);
1370
- }
1371
-
1372
- export interface TerminateJobResponse {
1373
- success: boolean;
1374
- message: string;
1375
- job_id: string;
1376
- deleted_checkpoints: number;
1377
- }
1378
-
1379
- export async function terminateJob(
1380
- jobId: string
1381
- ): Promise<ApiResult<TerminateJobResponse>> {
1382
- return request<TerminateJobResponse>(
1383
- "POST",
1384
- `/felix/training-jobs/${jobId}/terminate`
1385
- );
1386
- }
1387
-
1388
- export async function syncJob(jobId: string): Promise<ApiResult<TrainingJob>> {
1389
- return request<TrainingJob>("POST", `/felix/training-jobs/${jobId}/sync`);
1390
- }
1391
-
1392
- export interface Checkpoint {
1393
- id: string;
1394
- job_id: string;
1395
- epoch: number;
1396
- step?: number;
1397
- training_loss?: number;
1398
- validation_loss?: number;
1399
- accuracy?: number;
1400
- learning_rate?: number;
1401
- gpu_memory_used?: number;
1402
- gpu_memory_total?: number;
1403
- }
1404
-
1405
- export interface CheckpointListResponse {
1406
- success: boolean;
1407
- checkpoints: Checkpoint[];
1408
- count: number;
1409
- }
1410
-
1411
- export async function listCheckpoints(
1412
- jobId: string
1413
- ): Promise<ApiResult<CheckpointListResponse>> {
1414
- return request<CheckpointListResponse>(
1415
- "GET",
1416
- `/felix/training-jobs/${jobId}/checkpoints`
1417
- );
1418
- }
1419
-
1420
- export interface DeployCheckpointResponse {
1421
- success: boolean;
1422
- message: string;
1423
- job_id: string;
1424
- checkpoint_id: string;
1425
- mme_path: string;
1426
- }
1427
-
1428
- export async function deployCheckpoint(
1429
- jobId: string,
1430
- checkpointId: string
1431
- ): Promise<ApiResult<DeployCheckpointResponse>> {
1432
- return request<DeployCheckpointResponse>(
1433
- "POST",
1434
- `/felix/training-jobs/${jobId}/checkpoints/${checkpointId}/deploy`
1435
- );
1436
- }
1437
-
1438
- export interface UpdateModelNameResponse {
1439
- success: boolean;
1440
- message: string;
1441
- job_id: string;
1442
- model_name: string;
1443
- }
1444
-
1445
- export async function updateModelName(
1446
- jobId: string,
1447
- modelName: string
1448
- ): Promise<ApiResult<UpdateModelNameResponse>> {
1449
- return request<UpdateModelNameResponse>(
1450
- "PATCH",
1451
- `/felix/training-jobs/${jobId}/model-name`,
1452
- { model_name: modelName }
1453
- );
1454
- }
1455
-
1456
- // ─────────────────────────────────────────────────────────────────────────────
1457
- // Models
1458
- // ─────────────────────────────────────────────────────────────────────────────
1459
-
1460
- export interface DeployedModel {
1461
- id: string;
1462
- job_id: string | null;
1463
- model_name?: string;
1464
- s3_model_uri?: string;
1465
- mme_model_filename?: string;
1466
- endpoint_name?: string;
1467
- is_base_model: boolean;
1468
- deployed_at?: string | null;
1469
- last_used_at?: string | null;
1470
- invocation_count: number;
1471
- created_at?: string | null;
1472
- updated_at?: string | null;
1473
- }
1474
-
1475
- export interface DeployedModelsListResponse {
1476
- success: boolean;
1477
- models: DeployedModel[];
1478
- count: number;
1479
- }
1480
-
1481
- interface AnthropicCompatibleModelEntry {
1482
- id?: string;
1483
- display_name?: string;
1484
- type?: string;
1485
- created_at?: string | null;
1486
- created?: number;
1487
- owned_by?: string;
1488
- }
1489
-
1490
- interface AnthropicCompatibleModelsResponse {
1491
- data?: AnthropicCompatibleModelEntry[];
1492
- }
1493
-
1494
- function isLikelyJobId(value: unknown): value is string {
1495
- if (typeof value !== "string") return false;
1496
- return /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(value);
1497
- }
1498
-
1499
- export async function listModels(
1500
- options: { includeBase?: boolean } = {}
1501
- ): Promise<ApiResult<DeployedModelsListResponse>> {
1502
- const params = new URLSearchParams();
1503
- if (options.includeBase !== undefined) {
1504
- params.set("include_base", String(options.includeBase));
1505
- }
1506
- const query = params.toString();
1507
- const legacyUrl = query ? `/felix/models?${query}` : "/felix/models";
1508
- const legacy = await request<DeployedModelsListResponse>("GET", legacyUrl);
1509
- if (legacy.ok || legacy.status !== 404) {
1510
- return legacy;
1511
- }
1512
-
1513
- // Backend removed /felix/models. Fall back to the canonical
1514
- // Anthropic/OpenAI-compatible catalog and adapt to DeployedModel.
1515
- const fallback = await request<AnthropicCompatibleModelsResponse>("GET", "/v1/models");
1516
- if (!fallback.ok) {
1517
- return {
1518
- ok: false,
1519
- status: fallback.status,
1520
- error: fallback.error ?? "Failed to list deployed models.",
1521
- };
1522
- }
1523
- const entries = fallback.data?.data ?? [];
1524
- const models: DeployedModel[] = entries
1525
- .filter((entry) => entry && (options.includeBase || entry.owned_by !== "anthropic"))
1526
- .map((entry) => {
1527
- const id = typeof entry.id === "string" ? entry.id : "";
1528
- return {
1529
- id,
1530
- job_id: isLikelyJobId(id) ? id : null,
1531
- model_name: entry.display_name || id || undefined,
1532
- is_base_model: entry.owned_by === "anthropic" || entry.owned_by === "openai",
1533
- deployed_at: entry.created_at ?? null,
1534
- last_used_at: null,
1535
- invocation_count: 0,
1536
- created_at: entry.created_at ?? null,
1537
- updated_at: entry.created_at ?? null,
1538
- };
1539
- });
1540
-
1541
- return {
1542
- ok: true,
1543
- status: fallback.status,
1544
- data: {
1545
- success: true,
1546
- models,
1547
- count: models.length,
1548
- },
1549
- };
1550
- }
1551
-
1552
- export async function deleteModel(jobId: string): Promise<ApiResult> {
1553
- return request("DELETE", `/felix/models/${jobId}`);
1554
- }
1555
-
1556
- export interface TrainedModel {
1557
- id: string;
1558
- job_id?: string;
1559
- model_name?: string;
1560
- trained_model_path?: string;
1561
- datasets?: Array<{
1562
- name: string;
1563
- version: string;
1564
- }>;
1565
- base_model?: string;
1566
- validation_data_percentage?: number;
1567
- nr_epochs?: number;
1568
- learning_rate?: number;
1569
- batch_size?: number;
1570
- instance_type?: string;
1571
- status: string;
1572
- error_message?: string | null;
1573
- created_at?: string | null;
1574
- updated_at?: string | null;
1575
- started_at?: string | null;
1576
- completed_at?: string | null;
1577
- model_auto_selected?: boolean;
1578
- model_selection_reason?: string | null;
1579
- metrics?: {
1580
- final_training_loss?: number;
1581
- final_validation_loss?: number;
1582
- best_validation_loss?: number;
1583
- eval_f1_score?: number;
1584
- eval_precision?: number;
1585
- eval_recall?: number;
1586
- eval_accuracy?: number;
1587
- eval_validation_loss?: number;
1588
- };
1589
- }
1590
-
1591
- export interface TrainedModelsListResponse {
1592
- success: boolean;
1593
- training_jobs: TrainedModel[];
1594
- count: number;
1595
- }
1596
-
1597
- export async function listTrainedModels(): Promise<
1598
- ApiResult<TrainedModelsListResponse>
1599
- > {
1600
- return request<TrainedModelsListResponse>("GET", "/felix/trained-models");
1601
- }
1602
-
1603
- export interface AllModelsResponse {
1604
- deployed: DeployedModel[];
1605
- trained: TrainedModel[];
1606
- projects: ProjectResponse[];
1607
- }
1608
-
1609
- export async function listAllModels(): Promise<ApiResult<AllModelsResponse>> {
1610
- const [deployedResult, trainedResult, projectsResult] = await Promise.all([
1611
- listModels(),
1612
- listTrainedModels(),
1613
- listProjects(),
1614
- ]);
1615
-
1616
- if (!deployedResult.ok) {
1617
- return { ok: false, status: deployedResult.status, error: deployedResult.error };
1618
- }
1619
- if (!trainedResult.ok) {
1620
- return { ok: false, status: trainedResult.status, error: trainedResult.error };
1621
- }
1622
- if (!projectsResult.ok) {
1623
- return { ok: false, status: projectsResult.status, error: projectsResult.error };
1624
- }
1625
-
1626
- return {
1627
- ok: true,
1628
- status: 200,
1629
- data: {
1630
- deployed: deployedResult.data?.models ?? [],
1631
- trained: trainedResult.data?.training_jobs ?? [],
1632
- projects: projectsResult.data?.projects ?? [],
1633
- },
1634
- };
1635
- }
1636
-
1637
- export interface ModelDownloadResponse {
1638
- success?: boolean;
1639
- job_id?: string;
1640
- download_url: string;
1641
- expires_in_seconds?: number;
1642
- file_name?: string;
1643
- message?: string;
1644
- }
1645
-
1646
- export async function downloadModel(
1647
- jobId: string
1648
- ): Promise<ApiResult<ModelDownloadResponse>> {
1649
- return request<ModelDownloadResponse>(
1650
- "GET",
1651
- `/felix/training-jobs/${jobId}/download`
1652
- );
1653
- }
1654
-
1655
- // ─────────────────────────────────────────────────────────────────────────────
1656
- // Inference
1657
- // ─────────────────────────────────────────────────────────────────────────────
1658
-
1659
- export interface EncoderInferenceRequest {
1660
- model_id: string;
1661
- task: "extract_entities" | "classify_text" | "extract_json" | "schema";
1662
- text: string | string[];
1663
- schema: string[] | Record<string, unknown>;
1664
- threshold?: number;
1665
- include_confidence?: boolean;
1666
- include_spans?: boolean;
1667
- format_results?: boolean;
1668
- is_warmup?: boolean;
1669
- store?: boolean;
1670
- project_id?: string;
1671
- }
1672
-
1673
- export interface InferenceMessage {
1674
- role: "system" | "user" | "assistant";
1675
- content: string;
1676
- }
1677
-
1678
- export interface GenerateInferenceRequest {
1679
- model_id: string;
1680
- task: "generate";
1681
- messages: InferenceMessage[];
1682
- max_tokens?: number;
1683
- temperature?: number;
1684
- top_p?: number;
1685
- include_reasoning_trace?: boolean;
1686
- is_warmup?: boolean;
1687
- store?: boolean;
1688
- project_id?: string;
1689
- }
1690
-
1691
- export type InferenceRequest = EncoderInferenceRequest | GenerateInferenceRequest;
1692
-
1693
- export interface EncoderInferenceResponse {
1694
- type: "encoder";
1695
- inference_id: string;
1696
- result: Record<string, unknown> | unknown[];
1697
- model_id: string;
1698
- latency_ms: number;
1699
- token_usage: number;
1700
- model_used: string;
1701
- }
1702
-
1703
- export interface GenerateInferenceResponse {
1704
- type: "decoder";
1705
- inference_id: string;
1706
- completion: string;
1707
- reasoning_trace?: string | null;
1708
- model_id: string;
1709
- latency_ms: number;
1710
- }
1711
-
1712
- export type InferenceResponse = EncoderInferenceResponse | GenerateInferenceResponse;
1713
-
1714
- export async function runInference(
1715
- req: InferenceRequest
1716
- ): Promise<ApiResult<InferenceResponse>> {
1717
- return request<InferenceResponse>("POST", "/inference", req);
1718
- }
1719
-
1720
- export interface TextCompletionRequest {
1721
- model: string;
1722
- prompt: string;
1723
- temperature?: number;
1724
- max_tokens?: number;
1725
- stop?: string[] | string;
1726
- echo?: boolean;
1727
- extra_body?: Record<string, unknown>;
1728
- }
1729
-
1730
- export interface TextCompletionUsage {
1731
- prompt_tokens: number;
1732
- completion_tokens: number;
1733
- total_tokens: number;
1734
- }
1735
-
1736
- export interface TextCompletionChoice {
1737
- index?: number;
1738
- text: string;
1739
- finish_reason?: string | null;
1740
- }
1741
-
1742
- export interface TextCompletionResponse {
1743
- id?: string;
1744
- object?: string;
1745
- created?: number;
1746
- model: string;
1747
- choices: TextCompletionChoice[];
1748
- usage: TextCompletionUsage;
1749
- }
1750
-
1751
- export async function runTextCompletion(
1752
- req: TextCompletionRequest
1753
- ): Promise<ApiResult<TextCompletionResponse>> {
1754
- return request<TextCompletionResponse>("POST", "/v1/completions", req);
1755
- }
1756
-
1757
- export interface BaseModelInfo {
1758
- id: string;
1759
- name?: string;
1760
- label?: string;
1761
- /**
1762
- * One of "encoder" | "decoder" — the architectural family of the model.
1763
- * Note: backend used to expose this as `type`; the field is now `task_type`.
1764
- */
1765
- task_type?: string;
1766
- supports_inference?: boolean;
1767
- supports_on_demand_inference?: boolean;
1768
- supports_training?: boolean;
1769
- description?: string;
1770
- tier?: "enterprise" | "open" | string;
1771
- context_window?: number;
1772
- license?: string;
1773
- input_price_per_million?: number;
1774
- output_price_per_million?: number;
1775
- }
1776
-
1777
- export interface BaseModelsResponse {
1778
- models: BaseModelInfo[];
1779
- }
1780
-
1781
- export async function listBaseModels(): Promise<
1782
- ApiResult<BaseModelsResponse | BaseModelInfo[]>
1783
- > {
1784
- return request<BaseModelsResponse | BaseModelInfo[]>("GET", "/base-models");
1785
- }
1786
-
1787
- // ─────────────────────────────────────────────────────────────────────────────
1788
- // Agent chat
1789
- // ─────────────────────────────────────────────────────────────────────────────
1790
-
1791
- export interface AgentChatHistoryItem {
1792
- role: "user" | "assistant" | "system" | string;
1793
- content: string;
1794
- }
1795
-
1796
- export interface ChatSessionMessage {
1797
- id?: string;
1798
- session_id?: string;
1799
- role: string;
1800
- content: string;
1801
- message_index?: number;
1802
- created_at?: string;
1803
- tool_call_id?: string | null;
1804
- tool_calls?: Array<{
1805
- id?: string;
1806
- name?: string;
1807
- args?: Record<string, unknown>;
1808
- }> | null;
1809
- images?: Array<Record<string, unknown>> | null;
1810
- }
1811
-
1812
- export interface ChatSessionResponse {
1813
- id: string;
1814
- user_id: string;
1815
- title: string;
1816
- created_at: string;
1817
- updated_at: string;
1818
- is_archived: boolean;
1819
- project_id: string | null;
1820
- modal_sandbox_id: string | null;
1821
- raw_message_tree_present?: boolean;
1822
- messages: ChatSessionMessage[];
1823
- }
1824
-
1825
- export interface CreateChatSessionRequest {
1826
- first_message?: string;
1827
- title?: string;
1828
- project_id?: string;
1829
- }
1830
-
1831
- export interface UpdateChatSessionRequest {
1832
- title?: string;
1833
- is_archived?: boolean;
1834
- }
1835
-
1836
- export interface AppendSessionMessagesRequest {
1837
- messages: Array<{
1838
- role: string;
1839
- content: string;
1840
- tool_call_id?: string;
1841
- tool_calls?: Array<{
1842
- id?: string;
1843
- name?: string;
1844
- args?: Record<string, unknown>;
1845
- [key: string]: unknown;
1846
- }>;
1847
- }>;
1848
- }
1849
-
1850
- export interface ChatSessionListResponse {
1851
- success: boolean;
1852
- sessions: Array<Pick<ChatSessionResponse, "id" | "user_id" | "title" | "created_at" | "updated_at" | "is_archived">>;
1853
- }
1854
-
1855
- export interface AgentChatRequest {
1856
- message: string;
1857
- conversation_id?: string;
1858
- history?: AgentChatHistoryItem[];
1859
- resume_value?: string;
1860
- }
1861
-
1862
- export interface AgentChatResponse {
1863
- answer: string;
1864
- conversation_id: string;
1865
- tool_calls_made?: number;
1866
- }
1867
-
1868
- export interface AutoAgentRun {
1869
- agent_run_id: string;
1870
- status: string;
1871
- created_at?: string;
1872
- updated_at?: string;
1873
- project_id?: string;
1874
- model_id?: string;
1875
- result_url?: string;
1876
- run_type?: string;
1877
- [key: string]: unknown;
1878
- }
1879
-
1880
- export async function agentChat(
1881
- req: AgentChatRequest
1882
- ): Promise<ApiResult<AgentChatResponse>> {
1883
- const baseUrl = getBaseUrl().replace(/\/$/, "");
1884
- const apiKey = getApiKey();
1885
- const url = `${baseUrl}/auto-agent/clarify`;
1886
- const headers: Record<string, string> = {
1887
- "Content-Type": "application/json",
1888
- "User-Agent": "pioneer-cli/0.1.0",
1889
- };
1890
-
1891
- if (apiKey) {
1892
- headers["X-API-Key"] = apiKey;
1893
- }
1894
-
1895
- try {
1896
- const res = await fetch(url, {
1897
- method: "POST",
1898
- headers,
1899
- body: JSON.stringify(req),
1900
- });
1901
-
1902
- const text = await res.text();
1903
-
1904
- let parsedJson: unknown;
1905
- try {
1906
- parsedJson = JSON.parse(text);
1907
- } catch {
1908
- // Ignore; some responses may be SSE/event text or empty.
1909
- }
1910
-
1911
- const parseAgentStream = (raw: string): AgentChatResponse | undefined => {
1912
- const direct = parseAgentPayload(raw);
1913
- if (direct) return direct;
1914
-
1915
- const events = raw
1916
- .split("\n")
1917
- .map((line) => line.trim())
1918
- .filter((line) => line.startsWith("data:"));
1919
- const fallback: AgentChatResponse = {
1920
- answer: "",
1921
- conversation_id: "",
1922
- };
1923
-
1924
- for (const eventLine of events) {
1925
- const payload = eventLine.slice(5).trim();
1926
- if (!payload) continue;
1927
-
1928
- let event: Record<string, unknown> | undefined;
1929
- try {
1930
- event = JSON.parse(payload);
1931
- } catch {
1932
- continue;
1933
- }
1934
- if (typeof event === "object" && event) {
1935
- const type = typeof event.type === "string" ? event.type : undefined;
1936
-
1937
- if (type === "delta" && typeof event.content === "string") {
1938
- fallback.answer += event.content;
1939
- }
1940
-
1941
- if (typeof event.content === "string" && !fallback.answer) {
1942
- fallback.answer = event.content;
1943
- }
1944
-
1945
- if (typeof event.answer === "string") {
1946
- fallback.answer = event.answer;
1947
- }
1948
-
1949
- if (typeof event.conversation_id === "string") {
1950
- fallback.conversation_id = event.conversation_id;
1951
- }
1952
-
1953
- if (typeof event.tool_calls_made === "number") {
1954
- fallback.tool_calls_made = event.tool_calls_made;
1955
- }
1956
-
1957
- if (type === "complete") {
1958
- if (typeof event.conversation_id === "string") {
1959
- fallback.conversation_id = event.conversation_id;
1960
- }
1961
- if (typeof event.answer === "string") {
1962
- fallback.answer = event.answer;
1963
- }
1964
- }
1965
- }
1966
- }
1967
-
1968
- if (fallback.answer || fallback.conversation_id) {
1969
- return fallback;
1970
- }
1971
- return undefined;
1972
- };
1973
-
1974
- const parseAgentPayload = (raw: string): AgentChatResponse | undefined => {
1975
- if (!raw.trim()) {
1976
- return undefined;
1977
- }
1978
- if (typeof parsedJson !== "object" || parsedJson === null) {
1979
- return undefined;
1980
- }
1981
-
1982
- const payload = parsedJson as Record<string, unknown>;
1983
- const answer = typeof payload.answer === "string" ? payload.answer : "";
1984
- const conversationId =
1985
- typeof payload.answer === "string" && typeof payload.conversation_id === "string"
1986
- ? payload.conversation_id
1987
- : typeof payload.conversation_id === "string"
1988
- ? payload.conversation_id
1989
- : "";
1990
-
1991
- if (!answer && !conversationId && typeof payload.detail !== "string") {
1992
- return undefined;
1993
- }
1994
-
1995
- return {
1996
- answer,
1997
- conversation_id: conversationId,
1998
- ...(typeof payload.tool_calls_made === "number"
1999
- ? { tool_calls_made: payload.tool_calls_made }
2000
- : {}),
2001
- };
2002
- };
2003
-
2004
- const parsedResponse = parseAgentStream(text);
2005
-
2006
- if (!res.ok) {
2007
- const rawError = parsedJson ? JSON.stringify(parsedJson) : text || `HTTP ${res.status}`;
2008
- const extractedError = extractErrorMessage(parsedJson) || text || `HTTP ${res.status}`;
2009
- if (isAuthError(res.status, rawError)) {
2010
- return {
2011
- ok: false,
2012
- status: res.status,
2013
- error: formatAuthError(extractedError),
2014
- };
2015
- }
2016
- return {
2017
- ok: false,
2018
- status: res.status,
2019
- error: extractedError,
2020
- };
2021
- }
2022
-
2023
- if (parsedResponse) {
2024
- return { ok: true, status: res.status, data: parsedResponse };
2025
- }
2026
-
2027
- return {
2028
- ok: true,
2029
- status: res.status,
2030
- data: {
2031
- answer: text || "",
2032
- conversation_id: "",
2033
- },
2034
- };
2035
- } catch (err) {
2036
- return {
2037
- ok: false,
2038
- status: 0,
2039
- error: err instanceof Error ? err.message : String(err),
2040
- };
2041
- }
2042
- }
2043
-
2044
- // Agent session persistence APIs (Pioneer MLE agent)
2045
- export async function createAgentSession(
2046
- req: CreateChatSessionRequest
2047
- ): Promise<ApiResult<ChatSessionResponse>> {
2048
- return request<ChatSessionResponse>("POST", "/mle-agent/sessions", req);
2049
- }
2050
-
2051
- export async function getAgentSession(
2052
- sessionId: string
2053
- ): Promise<ApiResult<ChatSessionResponse>> {
2054
- return request<ChatSessionResponse>("GET", `/mle-agent/sessions/${sessionId}`);
2055
- }
2056
-
2057
- export async function listAgentSessions(): Promise<ApiResult<ChatSessionListResponse>> {
2058
- return request<ChatSessionListResponse>("GET", "/mle-agent/sessions");
2059
- }
2060
-
2061
- export async function appendSessionMessages(
2062
- sessionId: string,
2063
- req: AppendSessionMessagesRequest
2064
- ): Promise<ApiResult<{ success: boolean; session_id: string; messages_added: number }>> {
2065
- return request<{ success: boolean; session_id: string; messages_added: number }>(
2066
- "POST",
2067
- `/mle-agent/sessions/${sessionId}/messages`,
2068
- req
2069
- );
2070
- }
2071
-
2072
- export async function updateAgentSession(
2073
- sessionId: string,
2074
- req: UpdateChatSessionRequest
2075
- ): Promise<ApiResult<ChatSessionResponse>> {
2076
- return request<ChatSessionResponse>("PATCH", `/mle-agent/sessions/${sessionId}`, req);
2077
- }
2078
-
2079
- export async function deleteAgentSession(sessionId: string): Promise<ApiResult<{ success: boolean }>> {
2080
- return request<{ success: boolean }>("DELETE", `/mle-agent/sessions/${sessionId}`);
2081
- }
2082
-
2083
- export async function listAutoAgentRuns(
2084
- limit = 20,
2085
- offset = 0
2086
- ): Promise<ApiResult<{ runs: AutoAgentRun[] }>> {
2087
- return request<{ runs: AutoAgentRun[] }>(
2088
- "GET",
2089
- `/auto-agent/runs?limit=${limit}&offset=${offset}`
2090
- );
2091
- }
2092
-
2093
- export async function getAutoAgentRun(
2094
- runId: string
2095
- ): Promise<ApiResult<AutoAgentRun>> {
2096
- return request<AutoAgentRun>("GET", `/auto-agent/run/${runId}`);
2097
- }
2098
-
2099
- export async function stopAutoAgentRun(
2100
- runId: string
2101
- ): Promise<ApiResult<{ success: boolean }>> {
2102
- return request<{ success: boolean }>("POST", `/auto-agent/run/${runId}/stop`);
2103
- }
2104
-
2105
- // ─────────────────────────────────────────────────────────────────────────────
2106
- // Decoder Generation
2107
- // ─────────────────────────────────────────────────────────────────────────────
2108
-
2109
- export interface GenerateDecoderRequest {
2110
- domain_description: string;
2111
- instruction?: string;
2112
- num_examples?: number;
2113
- temperature?: number;
2114
- generation_profile?: "auto" | "fast" | "balanced" | "quality";
2115
- quality?: "light" | "medium" | "heavy";
2116
- include_reasoning_trace?: boolean;
2117
- reasoning_effort?: "low" | "medium" | "high";
2118
- constraints?: ConstraintRequest[];
2119
- save_dataset?: boolean;
2120
- dataset_name?: string;
2121
- project_id?: string;
2122
- use_meta_felix?: boolean;
2123
- session_id?: string;
2124
- config_num_examples?: number;
2125
- min_criteria?: number;
2126
- target_choices?: number;
2127
- split_ratio?: Record<string, number>;
2128
- negative_ratio?: number;
2129
- multiplicator?: Record<string, unknown>;
2130
- type?: "training" | "evaluation" | "split";
2131
- visibility?: "private" | "public";
2132
- }
2133
-
2134
- export async function generateDecoder(
2135
- req: GenerateDecoderRequest
2136
- ): Promise<ApiResult<GenerateJobStatus>> {
2137
- return generateDataset({
2138
- task_type: "decoder",
2139
- dataset_name: req.dataset_name ?? makeDefaultDatasetName("decoder"),
2140
- domain_description: req.domain_description,
2141
- instruction: req.instruction,
2142
- num_examples: req.num_examples,
2143
- temperature: req.temperature,
2144
- generation_profile: req.generation_profile,
2145
- quality: req.quality,
2146
- constraints: req.constraints,
2147
- project_id: req.project_id,
2148
- include_reasoning_trace: req.include_reasoning_trace,
2149
- reasoning_effort: req.reasoning_effort,
2150
- use_meta_felix: req.use_meta_felix,
2151
- session_id: req.session_id,
2152
- config_num_examples: req.config_num_examples,
2153
- min_criteria: req.min_criteria,
2154
- target_choices: req.target_choices,
2155
- split_ratio: req.split_ratio,
2156
- negative_ratio: req.negative_ratio,
2157
- multiplicator: req.multiplicator,
2158
- type: req.type,
2159
- visibility: req.visibility,
2160
- });
2161
- }
2162
-
2163
- // ─────────────────────────────────────────────────────────────────────────────
2164
- // Evaluations
2165
- // ─────────────────────────────────────────────────────────────────────────────
2166
-
2167
- export interface BaselineModel {
2168
- id: string;
2169
- name: string;
2170
- provider: string;
2171
- description: string;
2172
- }
2173
-
2174
- export interface BaselineModelsResponse {
2175
- success: boolean;
2176
- models: BaselineModel[];
2177
- count: number;
2178
- }
2179
-
2180
- export async function listBaselineModels(): Promise<ApiResult<BaselineModelsResponse>> {
2181
- return request<BaselineModelsResponse>("GET", "/felix/baseline-models");
2182
- }
2183
-
2184
- export interface Evaluation {
2185
- id: string;
2186
- user_id: string;
2187
- project_id?: string;
2188
- model_id: string;
2189
- dataset_name: string;
2190
- dataset_version: string;
2191
- provider?: string;
2192
- model_name?: string;
2193
- f1_score?: number;
2194
- precision_score?: number;
2195
- recall_score?: number;
2196
- accuracy?: number;
2197
- validation_loss?: number;
2198
- total_tokens?: number;
2199
- total_cost_usd?: number;
2200
- total_latency_ms?: number;
2201
- max_examples?: number;
2202
- seed?: number;
2203
- sample_count?: number;
2204
- evaluation_time_ms?: number;
2205
- status: string;
2206
- job_reference?: string;
2207
- error_message?: string;
2208
- created_at: string;
2209
- completed_at?: string;
2210
- }
2211
-
2212
- export interface EvaluationListResponse {
2213
- success: boolean;
2214
- evaluations: Evaluation[];
2215
- count: number;
2216
- }
2217
-
2218
- export async function listEvaluations(): Promise<
2219
- ApiResult<EvaluationListResponse>
2220
- > {
2221
- return request<EvaluationListResponse>("GET", "/felix/evaluations");
2222
- }
2223
-
2224
- export async function getEvaluation(
2225
- evalId: string
2226
- ): Promise<ApiResult<Evaluation>> {
2227
- return request<Evaluation>("GET", `/felix/evaluations/${evalId}`);
2228
- }
2229
-
2230
- export interface EvaluationCreateRequest {
2231
- dataset: DatasetRef;
2232
- model_id: string;
2233
- base_model?: string;
2234
- comparison_models?: string[];
2235
- dataset_names?: string[];
2236
- provider?: "felix" | "openai" | "together";
2237
- max_examples?: number;
2238
- seed?: number;
2239
- task_type?: string;
2240
- text_column?: string;
2241
- label_column?: string;
2242
- project_id?: string;
2243
- }
2244
-
2245
- export interface EvaluationCreateResponse {
2246
- success: boolean;
2247
- evaluations: Evaluation[];
2248
- count: number;
2249
- }
2250
-
2251
- export async function createEvaluation(
2252
- req: EvaluationCreateRequest
2253
- ): Promise<ApiResult<EvaluationCreateResponse>> {
2254
- const { dataset, ...rest } = req;
2255
- const baseModel = req.base_model ?? req.model_id;
2256
- return request<EvaluationCreateResponse>("POST", "/felix/evaluations", {
2257
- ...rest,
2258
- base_model: baseModel,
2259
- dataset_name: dataset.name,
2260
- dataset_version: dataset.version,
2261
- });
2262
- }
2263
-
2264
- export interface ModelWithEvaluation {
2265
- model_id: string;
2266
- model_name: string;
2267
- is_base_model: boolean;
2268
- evaluation?: Evaluation;
2269
- }
2270
-
2271
- export interface DatasetEvaluationsResponse {
2272
- success: boolean;
2273
- dataset_name: string;
2274
- dataset_version: string;
2275
- sample_count: number;
2276
- models: ModelWithEvaluation[];
2277
- count: number;
2278
- }
2279
-
2280
- export async function getDatasetEvaluations(
2281
- dataset: DatasetRef
2282
- ): Promise<ApiResult<DatasetEvaluationsResponse>> {
2283
- return request<DatasetEvaluationsResponse>(
2284
- "GET",
2285
- `/felix/datasets/${dataset.name}/${dataset.version}/evaluations`
2286
- );
2287
- }
2288
-
2289
- export interface DeleteEvaluationResponse {
2290
- success: boolean;
2291
- message: string;
2292
- }
2293
-
2294
- export async function deleteEvaluation(
2295
- evaluationId: string
2296
- ): Promise<ApiResult<DeleteEvaluationResponse>> {
2297
- return request<DeleteEvaluationResponse>("DELETE", `/felix/evaluations/${evaluationId}`);
2298
- }
2299
-
2300
- export interface UpdateEvaluationProjectRequest {
2301
- evaluation_id: string;
2302
- project_id?: string;
2303
- }
2304
-
2305
- export interface UpdateEvaluationProjectResponse {
2306
- success: boolean;
2307
- message: string;
2308
- }
2309
-
2310
- export async function updateEvaluationProject(
2311
- req: UpdateEvaluationProjectRequest
2312
- ): Promise<ApiResult<UpdateEvaluationProjectResponse>> {
2313
- return request<UpdateEvaluationProjectResponse>(
2314
- "PATCH",
2315
- `/felix/evaluations/${req.evaluation_id}/project`,
2316
- { project_id: req.project_id }
2317
- );
2318
- }
2319
-
2320
- // ─────────────────────────────────────────────────────────────────────────────
2321
- // Benchmarks
2322
- // ─────────────────────────────────────────────────────────────────────────────
2323
-
2324
- export interface BenchmarkInfo {
2325
- name: string;
2326
- description: string;
2327
- task: string;
2328
- dataset_source?: string;
2329
- metrics: string[];
2330
- }
2331
-
2332
- export interface ListBenchmarksResponse {
2333
- success: boolean;
2334
- benchmarks: Record<string, BenchmarkInfo[]>;
2335
- count: number;
2336
- }
2337
-
2338
- export async function listBenchmarks(): Promise<ApiResult<ListBenchmarksResponse>> {
2339
- return request<ListBenchmarksResponse>("GET", "/felix/benchmarks");
2340
- }
2341
-
2342
- export interface BenchmarkEvaluationRequest {
2343
- model_id: string;
2344
- task: "ner" | "text_classification";
2345
- benchmark: string;
2346
- max_samples?: number;
2347
- split?: string;
2348
- benchmark_config?: Record<string, unknown>;
2349
- }
2350
-
2351
- export interface BenchmarkEvaluationResponse {
2352
- success: boolean;
2353
- evaluation_id: string;
2354
- status: "pending" | "running" | "complete" | "errored";
2355
- task: string;
2356
- benchmark: string;
2357
- model_id: string;
2358
- metrics?: Record<string, unknown>;
2359
- error_message?: string;
2360
- created_at: string;
2361
- completed_at?: string;
2362
- }
2363
-
2364
- export async function startBenchmarkEvaluation(
2365
- req: BenchmarkEvaluationRequest
2366
- ): Promise<ApiResult<BenchmarkEvaluationResponse>> {
2367
- return request<BenchmarkEvaluationResponse>("POST", "/felix/benchmarks/evaluate", req);
2368
- }
2369
-
2370
- export async function getBenchmarkEvaluation(
2371
- evaluationId: string
2372
- ): Promise<ApiResult<BenchmarkEvaluationResponse>> {
2373
- return request<BenchmarkEvaluationResponse>(
2374
- "GET",
2375
- `/felix/benchmarks/evaluate/${evaluationId}`
2376
- );
2377
- }
2378
-
2379
- export async function cancelBenchmarkEvaluation(
2380
- evaluationId: string
2381
- ): Promise<ApiResult<{ success: boolean; message: string }>> {
2382
- return request<{ success: boolean; message: string }>(
2383
- "POST",
2384
- `/felix/benchmarks/evaluate/${evaluationId}/cancel`
2385
- );
2386
- }
2387
-
2388
- // ─────────────────────────────────────────────────────────────────────────────
2389
- // Data Editing (operates on persisted datasets)
2390
- // ─────────────────────────────────────────────────────────────────────────────
2391
-
2392
- export interface PIIFinding {
2393
- row_index: number;
2394
- column: string;
2395
- entity_type: string;
2396
- text: string;
2397
- start: number;
2398
- end: number;
2399
- score: number;
2400
- }
2401
-
2402
- export interface DataEditingScanRequest {
2403
- dataset: DatasetRef;
2404
- columns?: string[];
2405
- threshold?: number;
2406
- }
2407
-
2408
- export interface DataEditingScanResponse {
2409
- success: boolean;
2410
- dataset_id: string;
2411
- scan_type: "pii" | "phd";
2412
- findings_count: number;
2413
- affected_rows: number;
2414
- findings: PIIFinding[];
2415
- }
2416
-
2417
- export async function scanForPII(
2418
- req: DataEditingScanRequest
2419
- ): Promise<ApiResult<DataEditingScanResponse>> {
2420
- const { dataset, ...rest } = req;
2421
- return request<DataEditingScanResponse>("POST", "/felix/data-editing/scan-pii", {
2422
- ...rest,
2423
- dataset_name: dataset.name,
2424
- dataset_version: dataset.version,
2425
- });
2426
- }
2427
-
2428
- export async function scanForPHD(
2429
- req: DataEditingScanRequest
2430
- ): Promise<ApiResult<DataEditingScanResponse>> {
2431
- const { dataset, ...rest } = req;
2432
- return request<DataEditingScanResponse>("POST", "/felix/data-editing/scan-phd", {
2433
- ...rest,
2434
- dataset_name: dataset.name,
2435
- dataset_version: dataset.version,
2436
- });
2437
- }
2438
-
2439
- export interface DataEditingDismissOutlierRequest {
2440
- dataset: DatasetRef;
2441
- fingerprint: string;
2442
- }
2443
-
2444
- export interface DataEditingDismissOutlierResponse {
2445
- success: boolean;
2446
- }
2447
-
2448
- export async function dismissOutlier(
2449
- req: DataEditingDismissOutlierRequest
2450
- ): Promise<ApiResult<DataEditingDismissOutlierResponse>> {
2451
- const { dataset, ...rest } = req;
2452
- return request<DataEditingDismissOutlierResponse>("POST", "/felix/dataset/outliers/dismiss", {
2453
- ...rest,
2454
- dataset_name: dataset.name,
2455
- dataset_version: dataset.version,
2456
- });
2457
- }
2458
-
2459
- export interface DataEditingRemoveRequest {
2460
- dataset: DatasetRef;
2461
- findings: PIIFinding[];
2462
- redaction_method?: "redact" | "remove_row" | "mask";
2463
- save_as_new?: boolean;
2464
- }
2465
-
2466
- export interface DataEditingRemoveResponse {
2467
- success: boolean;
2468
- dataset_id: string;
2469
- new_dataset_id?: string;
2470
- rows_affected: number;
2471
- entities_removed: number;
2472
- message: string;
2473
- }
2474
-
2475
- export async function removePII(
2476
- req: DataEditingRemoveRequest
2477
- ): Promise<ApiResult<DataEditingRemoveResponse>> {
2478
- const { dataset, ...rest } = req;
2479
- return request<DataEditingRemoveResponse>("POST", "/felix/data-editing/remove-pii", {
2480
- ...rest,
2481
- dataset_name: dataset.name,
2482
- dataset_version: dataset.version,
2483
- });
2484
- }
2485
-
2486
- export interface DataEditingSubsampleRequest {
2487
- dataset: DatasetRef;
2488
- method?: "random" | "balanced" | "stratified";
2489
- n: number;
2490
- label_column?: string;
2491
- seed?: number;
2492
- save_as_new?: boolean;
2493
- }
2494
-
2495
- export interface DataEditingSubsampleResponse {
2496
- success: boolean;
2497
- dataset_id: string;
2498
- new_dataset_id: string;
2499
- original_rows: number;
2500
- new_rows: number;
2501
- method: string;
2502
- message: string;
2503
- }
2504
-
2505
- export async function subsampleDataset(
2506
- req: DataEditingSubsampleRequest
2507
- ): Promise<ApiResult<DataEditingSubsampleResponse>> {
2508
- const { dataset, ...rest } = req;
2509
- return request<DataEditingSubsampleResponse>("POST", "/felix/data-editing/subsample", {
2510
- ...rest,
2511
- dataset_name: dataset.name,
2512
- dataset_version: dataset.version,
2513
- });
2514
- }
2515
-
2516
- export interface DataEditingCheckLabelsRequest {
2517
- dataset: DatasetRef;
2518
- text_column: string;
2519
- label_column: string;
2520
- sample_size?: number;
2521
- }
2522
-
2523
- export interface LabelCheckResult {
2524
- row_index: number;
2525
- text: string;
2526
- current_label: string;
2527
- suggested_label: string;
2528
- confidence: number;
2529
- reasoning: string;
2530
- }
2531
-
2532
- export interface DataEditingCheckLabelsResponse {
2533
- success: boolean;
2534
- dataset_id: string;
2535
- checked_count: number;
2536
- issues_found: number;
2537
- results: LabelCheckResult[];
2538
- }
2539
-
2540
- export async function checkLabels(
2541
- req: DataEditingCheckLabelsRequest
2542
- ): Promise<ApiResult<DataEditingCheckLabelsResponse>> {
2543
- const { dataset, ...rest } = req;
2544
- return request<DataEditingCheckLabelsResponse>("POST", "/felix/data-editing/check-labels", {
2545
- ...rest,
2546
- dataset_name: dataset.name,
2547
- dataset_version: dataset.version,
2548
- });
2549
- }
2550
-
2551
- // ─────────────────────────────────────────────────────────────────────────────
2552
- // Hugging Face Integration
2553
- // ─────────────────────────────────────────────────────────────────────────────
2554
-
2555
- export interface HuggingFacePushRequest {
2556
- hf_token: string;
2557
- repo_id: string;
2558
- private?: boolean;
2559
- commit_message?: string;
2560
- }
2561
-
2562
- export interface HuggingFacePushResponse {
2563
- success: boolean;
2564
- url: string;
2565
- repo_id: string;
2566
- version: string;
2567
- message: string;
2568
- }
2569
-
2570
- export async function pushDatasetToHub(
2571
- dataset: DatasetRef,
2572
- options: HuggingFacePushRequest
2573
- ): Promise<ApiResult<HuggingFacePushResponse>> {
2574
- return request<HuggingFacePushResponse>(
2575
- "POST",
2576
- `/felix/datasets/${dataset.name}/${dataset.version}/push-to-hub`,
2577
- options
2578
- );
2579
- }
2580
-
2581
- export interface HuggingFacePushModelRequest {
2582
- hf_token: string;
2583
- repo_id: string;
2584
- private?: boolean;
2585
- commit_message?: string;
2586
- }
2587
-
2588
- export interface HuggingFacePushModelResponse {
2589
- success: boolean;
2590
- url: string;
2591
- repo_id: string;
2592
- job_id: string;
2593
- message: string;
2594
- }
2595
-
2596
- export async function pushModelToHub(
2597
- jobId: string,
2598
- options: HuggingFacePushModelRequest
2599
- ): Promise<ApiResult<HuggingFacePushModelResponse>> {
2600
- return request<HuggingFacePushModelResponse>(
2601
- "POST",
2602
- `/felix/training-jobs/${jobId}/push-to-hub`,
2603
- options
2604
- );
2605
- }
2606
-
2607
- export interface HuggingFacePullRequest {
2608
- repo_id: string;
2609
- hf_token: string;
2610
- revision?: string;
2611
- name?: string;
2612
- }
2613
-
2614
- export async function pullDatasetFromHub(
2615
- options: HuggingFacePullRequest
2616
- ): Promise<ApiResult<Dataset>> {
2617
- return request<Dataset>(
2618
- "POST",
2619
- "/felix/datasets/pull-from-hub",
2620
- options
2621
- );
2622
- }
2623
-
2624
- // ─────────────────────────────────────────────────────────────────────────────
2625
- // Constraints Generation
2626
- // ─────────────────────────────────────────────────────────────────────────────
2627
-
2628
- export interface GenerateNERConstraintsRequest {
2629
- labels: string[];
2630
- domain_description?: string;
2631
- min_criteria?: number;
2632
- }
2633
-
2634
- export interface GenerateClassificationConstraintsRequest {
2635
- labels: string[];
2636
- domain_description?: string;
2637
- min_criteria?: number;
2638
- }
2639
-
2640
- export interface GenerateRecordsConstraintsRequest {
2641
- fields: RecordField[];
2642
- domain_description?: string;
2643
- min_criteria?: number;
2644
- }
2645
-
2646
- export interface GenerateConstraintsResponse {
2647
- success: boolean;
2648
- constraints: ConstraintRequest[];
2649
- count: number;
2650
- token_usage?: number;
2651
- }
2652
-
2653
- export async function generateNERConstraints(
2654
- req: GenerateNERConstraintsRequest
2655
- ): Promise<ApiResult<GenerateConstraintsResponse>> {
2656
- return request<GenerateConstraintsResponse>(
2657
- "POST",
2658
- "/felix/constraints/ner",
2659
- req
2660
- );
2661
- }
2662
-
2663
- export async function generateClassificationConstraints(
2664
- req: GenerateClassificationConstraintsRequest
2665
- ): Promise<ApiResult<GenerateConstraintsResponse>> {
2666
- return request<GenerateConstraintsResponse>(
2667
- "POST",
2668
- "/felix/constraints/classification",
2669
- req
2670
- );
2671
- }
2672
-
2673
- export async function generateRecordsConstraints(
2674
- req: GenerateRecordsConstraintsRequest
2675
- ): Promise<ApiResult<GenerateConstraintsResponse>> {
2676
- return request<GenerateConstraintsResponse>(
2677
- "POST",
2678
- "/felix/constraints/records",
2679
- req
2680
- );
2681
- }
2682
-
2683
- export interface ExpandConstraintChoicesRequest {
2684
- constraint: ConstraintRequest;
2685
- task_description: string;
2686
- max_count?: number;
2687
- }
2688
-
2689
- export interface ExpandConstraintChoicesResponse {
2690
- success: boolean;
2691
- constraint: ConstraintRequest;
2692
- expanded_choices: string[];
2693
- count: number;
2694
- token_usage?: number;
2695
- }
2696
-
2697
- export async function expandConstraintChoices(
2698
- req: ExpandConstraintChoicesRequest
2699
- ): Promise<ApiResult<ExpandConstraintChoicesResponse>> {
2700
- return request<ExpandConstraintChoicesResponse>(
2701
- "POST",
2702
- "/felix/constraints/expand",
2703
- req
2704
- );
2705
- }
2706
-
2707
- // ─────────────────────────────────────────────────────────────────────────────
2708
- // Dataset Augmentation
2709
- // ─────────────────────────────────────────────────────────────────────────────
2710
-
2711
- export interface AugmentationOperation {
2712
- type: "remove_duplicates" | "remove_outliers" | "balance";
2713
- enabled: boolean;
2714
- }
2715
-
2716
- export interface DatasetAugmentationRequest {
2717
- task_type: "ner" | "classification";
2718
- operations: AugmentationOperation[];
2719
- new_dataset_name: string;
2720
- dataset_name?: string;
2721
- dataset_version?: string;
2722
- dataset?: Record<string, unknown>[];
2723
- target_distribution?: Record<string, number>;
2724
- domain_description?: string;
2725
- labels?: string[];
2726
- }
2727
-
2728
- export interface DatasetAugmentationResponse {
2729
- success: boolean;
2730
- original_dataset_name?: string;
2731
- original_dataset_version?: string;
2732
- new_dataset: Dataset;
2733
- modifications: Record<string, unknown>;
2734
- distribution_comparison: Record<string, unknown>[];
2735
- message: string;
2736
- }
2737
-
2738
- export async function augmentDataset(
2739
- req: DatasetAugmentationRequest
2740
- ): Promise<ApiResult<DatasetAugmentationResponse>> {
2741
- return request<DatasetAugmentationResponse>(
2742
- "POST",
2743
- "/felix/dataset/augment",
2744
- req
2745
- );
2746
- }
2747
-
2748
- // ─────────────────────────────────────────────────────────────────────────────
2749
- // Dataset Query
2750
- // ─────────────────────────────────────────────────────────────────────────────
2751
-
2752
- export interface DatasetQueryRequest {
2753
- dataset_name: string;
2754
- code: string;
2755
- version?: string;
2756
- timeout?: number;
2757
- max_rows_returned?: number;
2758
- }
2759
-
2760
- export interface DatasetQueryResponse {
2761
- success: boolean;
2762
- dataset_name: string;
2763
- dataset_version: string;
2764
- result: unknown;
2765
- result_type: "dataframe" | "series" | "scalar" | "string" | "none";
2766
- row_count?: number;
2767
- columns?: string[];
2768
- execution_time_ms: number;
2769
- truncated: boolean;
2770
- message: string;
2771
- }
2772
-
2773
- export async function queryDataset(
2774
- req: DatasetQueryRequest
2775
- ): Promise<ApiResult<DatasetQueryResponse>> {
2776
- return request<DatasetQueryResponse>("POST", "/felix/dataset/query", req);
2777
- }
2778
-
2779
- // ─────────────────────────────────────────────────────────────────────────────
2780
- // Dataset Grow
2781
- // ─────────────────────────────────────────────────────────────────────────────
2782
-
2783
- export interface GrowDatasetRequest {
2784
- dataset_id: string;
2785
- new_dataset_name: string;
2786
- target_size: number;
2787
- class_balance?: boolean;
2788
- domain_description?: string;
2789
- temperature?: number;
2790
- session_id?: string;
2791
- }
2792
-
2793
- export interface GrowDatasetResponse {
2794
- success: boolean;
2795
- dataset: Record<string, unknown>;
2796
- original_size: number;
2797
- new_size: number;
2798
- generated_count: number;
2799
- distribution: Record<string, number>;
2800
- token_usage?: number;
2801
- }
2802
-
2803
- export async function growDataset(
2804
- req: GrowDatasetRequest
2805
- ): Promise<ApiResult<GrowDatasetResponse>> {
2806
- return request<GrowDatasetResponse>("POST", "/felix/dataset/grow", req);
2807
- }
2808
-
2809
- // ─────────────────────────────────────────────────────────────────────────────
2810
- // Embeddings
2811
- // ─────────────────────────────────────────────────────────────────────────────
2812
-
2813
- export interface EmbeddingsRequest {
2814
- text: string;
2815
- model?: string;
2816
- }
2817
-
2818
- export interface EmbeddingsResponse {
2819
- embedding: number[];
2820
- }
2821
-
2822
- export async function createEmbeddings(
2823
- req: EmbeddingsRequest
2824
- ): Promise<ApiResult<EmbeddingsResponse>> {
2825
- return request<EmbeddingsResponse>("POST", "/felix/embeddings", req);
2826
- }
2827
-
2828
- // ─────────────────────────────────────────────────────────────────────────────
2829
- // Activity
2830
- // ─────────────────────────────────────────────────────────────────────────────
2831
-
2832
- export interface ActivityEvent {
2833
- id: string;
2834
- type: "project" | "dataset" | "model" | "evaluation" | "notebook" | "deployment";
2835
- name: string;
2836
- item_id: string;
2837
- created_by: string;
2838
- created_at: string;
2839
- project_id?: string;
2840
- }
2841
-
2842
- export interface ActivityLogResponse {
2843
- success: boolean;
2844
- events?: ActivityEvent[];
2845
- count: number;
2846
- }
2847
-
2848
- export async function listActivity(
2849
- options: { limit?: number } = {}
2850
- ): Promise<ApiResult<ActivityLogResponse>> {
2851
- const params = new URLSearchParams();
2852
- if (options.limit) params.set("limit", String(options.limit));
2853
- const query = params.toString();
2854
- const url = query ? `/felix/activity?${query}` : "/felix/activity";
2855
- return request<ActivityLogResponse>("GET", url);
2856
- }
2857
-
2858
- // ─────────────────────────────────────────────────────────────────────────────
2859
- // Projects
2860
- // ─────────────────────────────────────────────────────────────────────────────
2861
-
2862
- export interface ProjectCreateRequest {
2863
- name?: string;
2864
- icon?: string;
2865
- repo?: string;
2866
- description?: string;
2867
- active_model_id?: string;
2868
- selected_model_id?: string;
2869
- example?: Record<string, unknown>;
2870
- }
2871
-
2872
- export interface ProjectUpdateRequest {
2873
- name?: string;
2874
- icon?: string;
2875
- repo?: string;
2876
- description?: string;
2877
- selected_model_id?: string;
2878
- }
2879
-
2880
- export interface ProjectResponse {
2881
- id: string;
2882
- user_id: string;
2883
- name: string;
2884
- icon?: string;
2885
- repo?: string;
2886
- description?: string;
2887
- selected_model_id?: string;
2888
- example?: Record<string, unknown>;
2889
- created_at: string;
2890
- updated_at: string;
2891
- }
2892
-
2893
- export interface ProjectListResponse {
2894
- success: boolean;
2895
- projects: ProjectResponse[];
2896
- count: number;
2897
- }
2898
-
2899
- export interface ProjectDeleteResponse {
2900
- success: boolean;
2901
- message: string;
2902
- project_id: string;
2903
- }
2904
-
2905
- export interface ProjectDeploymentCreate {
2906
- training_job_id: string;
2907
- reason?: string;
2908
- }
2909
-
2910
- export interface ProjectDeploymentResponse {
2911
- /** Deployment record id. */
2912
- id?: string;
2913
- project_id?: string;
2914
- /** Set when a fine-tuned checkpoint was deployed. */
2915
- training_job_id?: string | null;
2916
- /**
2917
- * Set to the stock HuggingFace base model id when a base-model deploy was
2918
- * performed. May also be populated for training-job deploys (post backend
2919
- * enrichment) so the record always carries "what stock model is serving".
2920
- */
2921
- base_model?: string | null;
2922
- deployed_by?: string;
2923
- reason?: string;
2924
- deployed_at?: string;
2925
- // Legacy/optional fields kept for older response shapes.
2926
- success?: boolean;
2927
- message?: string;
2928
- deployment_id?: string;
2929
- }
2930
-
2931
- export interface ProjectDatasetCountResponse {
2932
- project_id: string;
2933
- dataset_count: number;
2934
- can_delete: boolean;
2935
- }
2936
-
2937
- export interface QualityMetricsResponse {
2938
- project_id: string;
2939
- pass_count: number;
2940
- fail_count: number;
2941
- uncertain_count: number;
2942
- total_judged: number;
2943
- pass_rate: number;
2944
- fail_rate: number;
2945
- }
2946
-
2947
- export async function listProjects(): Promise<ApiResult<ProjectListResponse>> {
2948
- return request<ProjectListResponse>("GET", "/projects");
2949
- }
2950
-
2951
- export async function createProject(
2952
- req: ProjectCreateRequest
2953
- ): Promise<ApiResult<ProjectResponse>> {
2954
- return request<ProjectResponse>("POST", "/projects", req);
2955
- }
2956
-
2957
- export async function getProject(projectId: string): Promise<ApiResult<ProjectResponse>> {
2958
- return request<ProjectResponse>("GET", `/projects/${projectId}`);
2959
- }
2960
-
2961
- export async function updateProject(
2962
- projectId: string,
2963
- req: ProjectUpdateRequest
2964
- ): Promise<ApiResult<ProjectResponse>> {
2965
- return request<ProjectResponse>("PATCH", `/projects/${projectId}`, req);
2966
- }
2967
-
2968
- export async function deleteProject(
2969
- projectId: string
2970
- ): Promise<ApiResult<ProjectDeleteResponse>> {
2971
- return request<ProjectDeleteResponse>("DELETE", `/projects/${projectId}`);
2972
- }
2973
-
2974
- export async function getProjectDatasetCount(
2975
- projectId: string
2976
- ): Promise<ApiResult<ProjectDatasetCountResponse>> {
2977
- return request<ProjectDatasetCountResponse>(
2978
- "GET",
2979
- `/projects/${projectId}/dataset-count`
2980
- );
2981
- }
2982
-
2983
- export async function getProjectQualityMetrics(
2984
- projectId: string
2985
- ): Promise<ApiResult<QualityMetricsResponse>> {
2986
- return request<QualityMetricsResponse>(
2987
- "GET",
2988
- `/projects/${projectId}/quality-metrics`
2989
- );
2990
- }
2991
-
2992
- export async function deployTrainingJobToProject(
2993
- projectId: string,
2994
- req: ProjectDeploymentCreate
2995
- ): Promise<ApiResult<ProjectDeploymentResponse>> {
2996
- return request<ProjectDeploymentResponse>(
2997
- "POST",
2998
- `/projects/${projectId}/deployments`,
2999
- req
3000
- );
3001
- }
3002
-
3003
- export async function rollbackProjectDeployment(
3004
- projectId: string,
3005
- deploymentId: string
3006
- ): Promise<ApiResult<ProjectDeploymentResponse>> {
3007
- return request<ProjectDeploymentResponse>(
3008
- "POST",
3009
- `/projects/${projectId}/deployments/${deploymentId}/rollback`
3010
- );
3011
- }
3012
-
3013
- // ─────────────────────────────────────────────────────────────────────────────
3014
- // Presets
3015
- // ─────────────────────────────────────────────────────────────────────────────
3016
-
3017
- export interface PresetMetadata {
3018
- id: string;
3019
- name: string;
3020
- description: string;
3021
- task_type: string;
3022
- tags?: string[];
3023
- }
3024
-
3025
- export interface PresetDetail {
3026
- id: string;
3027
- name: string;
3028
- description: string;
3029
- task_type: string;
3030
- config: Record<string, unknown>;
3031
- tags?: string[];
3032
- }
3033
-
3034
- export async function listPresets(): Promise<ApiResult<PresetMetadata[]>> {
3035
- return request<PresetMetadata[]>("GET", "/felix/presets");
3036
- }
3037
-
3038
- export async function getPreset(
3039
- presetId: string
3040
- ): Promise<ApiResult<PresetDetail>> {
3041
- return request<PresetDetail>("GET", `/felix/presets/${presetId}`);
3042
- }
3043
-
3044
- // ─────────────────────────────────────────────────────────────────────────────
3045
- // Adaptive Annotation
3046
- // ─────────────────────────────────────────────────────────────────────────────
3047
-
3048
- export interface StartAnnotationSessionRequest {
3049
- column_name: string;
3050
- input_columns: string[];
3051
- task_description: string;
3052
- task_type?: "classification" | "scoring" | "entity_extraction" | "json_extraction";
3053
- output_dataset_name?: string;
3054
- possible_labels?: Array<{ name: string; description?: string }>;
3055
- scoring_criteria?: Array<Record<string, unknown>>;
3056
- entity_types?: Array<{ name: string; description?: string }>;
3057
- json_schema?: Array<Record<string, unknown>>;
3058
- auto_accept_threshold?: number;
3059
- }
3060
-
3061
- export interface StartAnnotationSessionResponse {
3062
- success: boolean;
3063
- session_id: string;
3064
- dataset_id: string;
3065
- task_type: string;
3066
- total_items: number;
3067
- }
3068
-
3069
- export async function startAnnotationSession(
3070
- datasetId: string,
3071
- req: StartAnnotationSessionRequest
3072
- ): Promise<ApiResult<StartAnnotationSessionResponse>> {
3073
- return request<StartAnnotationSessionResponse>(
3074
- "POST",
3075
- `/felix/datasets/annotate/${datasetId}/start`,
3076
- req
3077
- );
3078
- }
3079
-
3080
- export interface NextAnnotationItemResponse {
3081
- complete: boolean;
3082
- rate_limited?: boolean;
3083
- auto_accepted_in_call?: number;
3084
- item_id?: number;
3085
- item_data?: Record<string, unknown>;
3086
- task_type?: string;
3087
- task_description?: string;
3088
- possible_labels?: string[];
3089
- scoring_criteria?: Array<Record<string, unknown>>;
3090
- entity_types?: Array<Record<string, unknown>>;
3091
- json_schema?: Array<Record<string, unknown>>;
3092
- suggestion?: Record<string, unknown>;
3093
- stats?: Record<string, unknown>;
3094
- }
3095
-
3096
- export async function getNextAnnotationItem(
3097
- datasetId: string,
3098
- options: { maxAutoAccept?: number } = {}
3099
- ): Promise<ApiResult<NextAnnotationItemResponse>> {
3100
- const params = new URLSearchParams();
3101
- if (options.maxAutoAccept !== undefined) {
3102
- params.set("max_auto_accept", String(options.maxAutoAccept));
3103
- }
3104
- const query = params.toString();
3105
- const url = `/felix/datasets/annotate/${datasetId}/next${query ? `?${query}` : ""}`;
3106
- return request<NextAnnotationItemResponse>("GET", url);
3107
- }
3108
-
3109
- export interface AnnotationFeedbackRequest {
3110
- item_id: number;
3111
- thumb: "up" | "down";
3112
- corrected_label?: string;
3113
- corrected_criteria?: Record<string, string>;
3114
- corrected_entities?: string[][];
3115
- corrected_json?: Record<string, unknown>;
3116
- }
3117
-
3118
- export interface AnnotationFeedbackResponse {
3119
- success: boolean;
3120
- item_id: number;
3121
- action: "up" | "down";
3122
- was_correct: boolean;
3123
- similar_items_queued?: number;
3124
- stats?: Record<string, unknown>;
3125
- }
3126
-
3127
- export async function submitAnnotationFeedback(
3128
- datasetId: string,
3129
- req: AnnotationFeedbackRequest
3130
- ): Promise<ApiResult<AnnotationFeedbackResponse>> {
3131
- return request<AnnotationFeedbackResponse>(
3132
- "POST",
3133
- `/felix/datasets/annotate/${datasetId}/feedback`,
3134
- req
3135
- );
3136
- }
3137
-
3138
- export interface AnnotationStatsResponse {
3139
- total_items: number;
3140
- annotated: number;
3141
- remaining: number;
3142
- auto_accepted: number;
3143
- human_annotated: number;
3144
- auto_accept_rate: number;
3145
- label_stats?: Record<string, unknown>;
3146
- }
3147
-
3148
- export async function getAnnotationStats(
3149
- datasetId: string
3150
- ): Promise<ApiResult<AnnotationStatsResponse>> {
3151
- return request<AnnotationStatsResponse>(
3152
- "GET",
3153
- `/felix/datasets/annotate/${datasetId}/stats`
3154
- );
3155
- }
3156
-
3157
- export interface AnnotationDownloadResponse {
3158
- data: Record<string, unknown>[];
3159
- columns: string[];
3160
- rows: number;
3161
- stats?: AnnotationStatsResponse;
3162
- }
3163
-
3164
- export async function downloadAnnotatedData(
3165
- datasetId: string
3166
- ): Promise<ApiResult<AnnotationDownloadResponse>> {
3167
- return request<AnnotationDownloadResponse>(
3168
- "GET",
3169
- `/felix/datasets/annotate/${datasetId}/download`
3170
- );
3171
- }
3172
-
3173
- export interface EndAnnotationSessionResponse {
3174
- success: boolean;
3175
- message: string;
3176
- final_stats?: AnnotationStatsResponse;
3177
- }
3178
-
3179
- export async function endAnnotationSession(
3180
- datasetId: string
3181
- ): Promise<ApiResult<EndAnnotationSessionResponse>> {
3182
- return request<EndAnnotationSessionResponse>(
3183
- "DELETE",
3184
- `/felix/datasets/annotate/${datasetId}/session`
3185
- );
3186
- }
3187
-