@fastino-ai/pioneer-cli 0.2.9 → 0.2.11

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,3086 +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 } = {}
1275
- ): Promise<ApiResult<TrainingJobListResponse>> {
1276
- const params = new URLSearchParams();
1277
- if (options.status) params.set("status", options.status);
1278
- const query = params.toString();
1279
- const url = query ? `/felix/training-jobs?${query}` : "/felix/training-jobs";
1280
- return request<TrainingJobListResponse>("GET", url);
1281
- }
1282
-
1283
- export async function getJob(jobId: string): Promise<ApiResult<TrainingJob>> {
1284
- return request<TrainingJob>("GET", `/felix/training-jobs/${jobId}`);
1285
- }
1286
-
1287
- export interface TrainingJobCreateRequest {
1288
- model_name: string;
1289
- datasets: DatasetRef[];
1290
- base_model?: string;
1291
- auto_select_model?: boolean;
1292
- validation_data_percentage?: number;
1293
- nr_epochs?: number;
1294
- learning_rate?: number;
1295
- batch_size?: number;
1296
- save_steps?: number;
1297
- wandb_api_key?: string;
1298
- project_id?: string;
1299
- training_type?: "full" | "lora";
1300
- lora_r?: number;
1301
- lora_alpha?: number;
1302
- lora_dropout?: number;
1303
- enable_probe_override?: boolean;
1304
- probe_delta_threshold?: number;
1305
- }
1306
-
1307
- export async function createJob(
1308
- req: TrainingJobCreateRequest
1309
- ): Promise<ApiResult<TrainingJob>> {
1310
- return request<TrainingJob>("POST", "/felix/training-jobs", req);
1311
- }
1312
-
1313
- export interface TrainingLog {
1314
- id: string;
1315
- job_id: string;
1316
- timestamp: string;
1317
- level: string;
1318
- message: string;
1319
- source: string;
1320
- }
1321
-
1322
- export interface TrainingLogsResponse {
1323
- job_id: string;
1324
- logs: TrainingLog[];
1325
- total_logs: number;
1326
- }
1327
-
1328
- export async function getJobLogs(
1329
- jobId: string
1330
- ): Promise<ApiResult<TrainingLogsResponse>> {
1331
- return request<TrainingLogsResponse>("GET", `/felix/training-jobs/${jobId}/logs`);
1332
- }
1333
-
1334
- export async function deleteJob(jobId: string): Promise<ApiResult> {
1335
- return request("DELETE", `/felix/training-jobs/${jobId}`);
1336
- }
1337
-
1338
- export interface StopJobResponse {
1339
- success: boolean;
1340
- message: string;
1341
- job_id: string;
1342
- status: string;
1343
- }
1344
-
1345
- export async function stopJob(
1346
- jobId: string
1347
- ): Promise<ApiResult<StopJobResponse>> {
1348
- return request<StopJobResponse>("POST", `/felix/training-jobs/${jobId}/stop`);
1349
- }
1350
-
1351
- export interface TerminateJobResponse {
1352
- success: boolean;
1353
- message: string;
1354
- job_id: string;
1355
- deleted_checkpoints: number;
1356
- }
1357
-
1358
- export async function terminateJob(
1359
- jobId: string
1360
- ): Promise<ApiResult<TerminateJobResponse>> {
1361
- return request<TerminateJobResponse>(
1362
- "POST",
1363
- `/felix/training-jobs/${jobId}/terminate`
1364
- );
1365
- }
1366
-
1367
- export async function syncJob(jobId: string): Promise<ApiResult<TrainingJob>> {
1368
- return request<TrainingJob>("POST", `/felix/training-jobs/${jobId}/sync`);
1369
- }
1370
-
1371
- export interface Checkpoint {
1372
- id: string;
1373
- job_id: string;
1374
- epoch: number;
1375
- step?: number;
1376
- training_loss?: number;
1377
- validation_loss?: number;
1378
- accuracy?: number;
1379
- learning_rate?: number;
1380
- gpu_memory_used?: number;
1381
- gpu_memory_total?: number;
1382
- }
1383
-
1384
- export interface CheckpointListResponse {
1385
- success: boolean;
1386
- checkpoints: Checkpoint[];
1387
- count: number;
1388
- }
1389
-
1390
- export async function listCheckpoints(
1391
- jobId: string
1392
- ): Promise<ApiResult<CheckpointListResponse>> {
1393
- return request<CheckpointListResponse>(
1394
- "GET",
1395
- `/felix/training-jobs/${jobId}/checkpoints`
1396
- );
1397
- }
1398
-
1399
- export interface DeployCheckpointResponse {
1400
- success: boolean;
1401
- message: string;
1402
- job_id: string;
1403
- checkpoint_id: string;
1404
- mme_path: string;
1405
- }
1406
-
1407
- export async function deployCheckpoint(
1408
- jobId: string,
1409
- checkpointId: string
1410
- ): Promise<ApiResult<DeployCheckpointResponse>> {
1411
- return request<DeployCheckpointResponse>(
1412
- "POST",
1413
- `/felix/training-jobs/${jobId}/checkpoints/${checkpointId}/deploy`
1414
- );
1415
- }
1416
-
1417
- export interface UpdateModelNameResponse {
1418
- success: boolean;
1419
- message: string;
1420
- job_id: string;
1421
- model_name: string;
1422
- }
1423
-
1424
- export async function updateModelName(
1425
- jobId: string,
1426
- modelName: string
1427
- ): Promise<ApiResult<UpdateModelNameResponse>> {
1428
- return request<UpdateModelNameResponse>(
1429
- "PATCH",
1430
- `/felix/training-jobs/${jobId}/model-name`,
1431
- { model_name: modelName }
1432
- );
1433
- }
1434
-
1435
- // ─────────────────────────────────────────────────────────────────────────────
1436
- // Models
1437
- // ─────────────────────────────────────────────────────────────────────────────
1438
-
1439
- export interface DeployedModel {
1440
- id: string;
1441
- job_id: string | null;
1442
- model_name?: string;
1443
- s3_model_uri?: string;
1444
- mme_model_filename?: string;
1445
- endpoint_name?: string;
1446
- is_base_model: boolean;
1447
- deployed_at?: string | null;
1448
- last_used_at?: string | null;
1449
- invocation_count: number;
1450
- created_at?: string | null;
1451
- updated_at?: string | null;
1452
- }
1453
-
1454
- export interface DeployedModelsListResponse {
1455
- success: boolean;
1456
- models: DeployedModel[];
1457
- count: number;
1458
- }
1459
-
1460
- export async function listModels(
1461
- options: { includeBase?: boolean } = {}
1462
- ): Promise<ApiResult<DeployedModelsListResponse>> {
1463
- const params = new URLSearchParams();
1464
- if (options.includeBase !== undefined) {
1465
- params.set("include_base", String(options.includeBase));
1466
- }
1467
- const query = params.toString();
1468
- const url = query ? `/felix/models?${query}` : "/felix/models";
1469
- return request<DeployedModelsListResponse>("GET", url);
1470
- }
1471
-
1472
- export async function deleteModel(jobId: string): Promise<ApiResult> {
1473
- return request("DELETE", `/felix/models/${jobId}`);
1474
- }
1475
-
1476
- export interface TrainedModel {
1477
- id: string;
1478
- job_id?: string;
1479
- model_name?: string;
1480
- trained_model_path?: string;
1481
- datasets?: Array<{
1482
- name: string;
1483
- version: string;
1484
- }>;
1485
- base_model?: string;
1486
- validation_data_percentage?: number;
1487
- nr_epochs?: number;
1488
- learning_rate?: number;
1489
- batch_size?: number;
1490
- instance_type?: string;
1491
- status: string;
1492
- error_message?: string | null;
1493
- created_at?: string | null;
1494
- updated_at?: string | null;
1495
- started_at?: string | null;
1496
- completed_at?: string | null;
1497
- model_auto_selected?: boolean;
1498
- model_selection_reason?: string | null;
1499
- metrics?: {
1500
- final_training_loss?: number;
1501
- final_validation_loss?: number;
1502
- best_validation_loss?: number;
1503
- eval_f1_score?: number;
1504
- eval_precision?: number;
1505
- eval_recall?: number;
1506
- eval_accuracy?: number;
1507
- eval_validation_loss?: number;
1508
- };
1509
- }
1510
-
1511
- export interface TrainedModelsListResponse {
1512
- success: boolean;
1513
- training_jobs: TrainedModel[];
1514
- count: number;
1515
- }
1516
-
1517
- export async function listTrainedModels(): Promise<
1518
- ApiResult<TrainedModelsListResponse>
1519
- > {
1520
- return request<TrainedModelsListResponse>("GET", "/felix/trained-models");
1521
- }
1522
-
1523
- export interface AllModelsResponse {
1524
- deployed: DeployedModel[];
1525
- trained: TrainedModel[];
1526
- projects: ProjectResponse[];
1527
- }
1528
-
1529
- export async function listAllModels(): Promise<ApiResult<AllModelsResponse>> {
1530
- const [deployedResult, trainedResult, projectsResult] = await Promise.all([
1531
- listModels(),
1532
- listTrainedModels(),
1533
- listProjects(),
1534
- ]);
1535
-
1536
- if (!deployedResult.ok) {
1537
- return { ok: false, status: deployedResult.status, error: deployedResult.error };
1538
- }
1539
- if (!trainedResult.ok) {
1540
- return { ok: false, status: trainedResult.status, error: trainedResult.error };
1541
- }
1542
- if (!projectsResult.ok) {
1543
- return { ok: false, status: projectsResult.status, error: projectsResult.error };
1544
- }
1545
-
1546
- return {
1547
- ok: true,
1548
- status: 200,
1549
- data: {
1550
- deployed: deployedResult.data?.models ?? [],
1551
- trained: trainedResult.data?.training_jobs ?? [],
1552
- projects: projectsResult.data?.projects ?? [],
1553
- },
1554
- };
1555
- }
1556
-
1557
- export interface ModelDownloadResponse {
1558
- success?: boolean;
1559
- job_id?: string;
1560
- download_url: string;
1561
- expires_in_seconds?: number;
1562
- file_name?: string;
1563
- message?: string;
1564
- }
1565
-
1566
- export async function downloadModel(
1567
- jobId: string
1568
- ): Promise<ApiResult<ModelDownloadResponse>> {
1569
- return request<ModelDownloadResponse>(
1570
- "GET",
1571
- `/felix/training-jobs/${jobId}/download`
1572
- );
1573
- }
1574
-
1575
- // ─────────────────────────────────────────────────────────────────────────────
1576
- // Inference
1577
- // ─────────────────────────────────────────────────────────────────────────────
1578
-
1579
- export interface EncoderInferenceRequest {
1580
- model_id: string;
1581
- task: "extract_entities" | "classify_text" | "extract_json" | "schema";
1582
- text: string | string[];
1583
- schema: string[] | Record<string, unknown>;
1584
- threshold?: number;
1585
- include_confidence?: boolean;
1586
- include_spans?: boolean;
1587
- format_results?: boolean;
1588
- is_warmup?: boolean;
1589
- store?: boolean;
1590
- project_id?: string;
1591
- }
1592
-
1593
- export interface InferenceMessage {
1594
- role: "system" | "user" | "assistant";
1595
- content: string;
1596
- }
1597
-
1598
- export interface GenerateInferenceRequest {
1599
- model_id: string;
1600
- task: "generate";
1601
- messages: InferenceMessage[];
1602
- max_tokens?: number;
1603
- temperature?: number;
1604
- top_p?: number;
1605
- include_reasoning_trace?: boolean;
1606
- is_warmup?: boolean;
1607
- store?: boolean;
1608
- project_id?: string;
1609
- }
1610
-
1611
- export type InferenceRequest = EncoderInferenceRequest | GenerateInferenceRequest;
1612
-
1613
- export interface EncoderInferenceResponse {
1614
- type: "encoder";
1615
- inference_id: string;
1616
- result: Record<string, unknown> | unknown[];
1617
- model_id: string;
1618
- latency_ms: number;
1619
- token_usage: number;
1620
- model_used: string;
1621
- }
1622
-
1623
- export interface GenerateInferenceResponse {
1624
- type: "decoder";
1625
- inference_id: string;
1626
- completion: string;
1627
- reasoning_trace?: string | null;
1628
- model_id: string;
1629
- latency_ms: number;
1630
- }
1631
-
1632
- export type InferenceResponse = EncoderInferenceResponse | GenerateInferenceResponse;
1633
-
1634
- export async function runInference(
1635
- req: InferenceRequest
1636
- ): Promise<ApiResult<InferenceResponse>> {
1637
- return request<InferenceResponse>("POST", "/inference", req);
1638
- }
1639
-
1640
- export interface TextCompletionRequest {
1641
- model: string;
1642
- prompt: string;
1643
- temperature?: number;
1644
- max_tokens?: number;
1645
- stop?: string[] | string;
1646
- echo?: boolean;
1647
- extra_body?: Record<string, unknown>;
1648
- }
1649
-
1650
- export interface TextCompletionUsage {
1651
- prompt_tokens: number;
1652
- completion_tokens: number;
1653
- total_tokens: number;
1654
- }
1655
-
1656
- export interface TextCompletionChoice {
1657
- index?: number;
1658
- text: string;
1659
- finish_reason?: string | null;
1660
- }
1661
-
1662
- export interface TextCompletionResponse {
1663
- id?: string;
1664
- object?: string;
1665
- created?: number;
1666
- model: string;
1667
- choices: TextCompletionChoice[];
1668
- usage: TextCompletionUsage;
1669
- }
1670
-
1671
- export async function runTextCompletion(
1672
- req: TextCompletionRequest
1673
- ): Promise<ApiResult<TextCompletionResponse>> {
1674
- return request<TextCompletionResponse>("POST", "/v1/completions", req);
1675
- }
1676
-
1677
- export interface BaseModelInfo {
1678
- id: string;
1679
- name?: string;
1680
- label?: string;
1681
- type?: "encoder" | "decoder" | string;
1682
- task_type?: string;
1683
- supports_inference?: boolean;
1684
- supports_on_demand_inference?: boolean;
1685
- supports_training?: boolean;
1686
- description?: string;
1687
- }
1688
-
1689
- export interface BaseModelsResponse {
1690
- models: BaseModelInfo[];
1691
- }
1692
-
1693
- export async function listBaseModels(): Promise<
1694
- ApiResult<BaseModelsResponse | BaseModelInfo[]>
1695
- > {
1696
- return request<BaseModelsResponse | BaseModelInfo[]>("GET", "/base-models");
1697
- }
1698
-
1699
- // ─────────────────────────────────────────────────────────────────────────────
1700
- // Agent chat
1701
- // ─────────────────────────────────────────────────────────────────────────────
1702
-
1703
- export interface AgentChatHistoryItem {
1704
- role: "user" | "assistant" | "system" | string;
1705
- content: string;
1706
- }
1707
-
1708
- export interface ChatSessionMessage {
1709
- id?: string;
1710
- session_id?: string;
1711
- role: string;
1712
- content: string;
1713
- message_index?: number;
1714
- created_at?: string;
1715
- tool_call_id?: string | null;
1716
- tool_calls?: Array<{
1717
- id?: string;
1718
- name?: string;
1719
- args?: Record<string, unknown>;
1720
- }> | null;
1721
- images?: Array<Record<string, unknown>> | null;
1722
- }
1723
-
1724
- export interface ChatSessionResponse {
1725
- id: string;
1726
- user_id: string;
1727
- title: string;
1728
- created_at: string;
1729
- updated_at: string;
1730
- is_archived: boolean;
1731
- project_id: string | null;
1732
- modal_sandbox_id: string | null;
1733
- raw_message_tree_present?: boolean;
1734
- messages: ChatSessionMessage[];
1735
- }
1736
-
1737
- export interface CreateChatSessionRequest {
1738
- first_message?: string;
1739
- title?: string;
1740
- project_id?: string;
1741
- }
1742
-
1743
- export interface UpdateChatSessionRequest {
1744
- title?: string;
1745
- is_archived?: boolean;
1746
- }
1747
-
1748
- export interface AppendSessionMessagesRequest {
1749
- messages: Array<{
1750
- role: string;
1751
- content: string;
1752
- tool_call_id?: string;
1753
- tool_calls?: Array<{
1754
- id?: string;
1755
- name?: string;
1756
- args?: Record<string, unknown>;
1757
- [key: string]: unknown;
1758
- }>;
1759
- }>;
1760
- }
1761
-
1762
- export interface ChatSessionListResponse {
1763
- success: boolean;
1764
- sessions: Array<Pick<ChatSessionResponse, "id" | "user_id" | "title" | "created_at" | "updated_at" | "is_archived">>;
1765
- }
1766
-
1767
- export interface AgentChatRequest {
1768
- message: string;
1769
- conversation_id?: string;
1770
- history?: AgentChatHistoryItem[];
1771
- resume_value?: string;
1772
- }
1773
-
1774
- export interface AgentChatResponse {
1775
- answer: string;
1776
- conversation_id: string;
1777
- tool_calls_made?: number;
1778
- }
1779
-
1780
- export interface AutoAgentRun {
1781
- agent_run_id: string;
1782
- status: string;
1783
- created_at?: string;
1784
- updated_at?: string;
1785
- project_id?: string;
1786
- model_id?: string;
1787
- result_url?: string;
1788
- run_type?: string;
1789
- [key: string]: unknown;
1790
- }
1791
-
1792
- export async function agentChat(
1793
- req: AgentChatRequest
1794
- ): Promise<ApiResult<AgentChatResponse>> {
1795
- const baseUrl = getBaseUrl().replace(/\/$/, "");
1796
- const apiKey = getApiKey();
1797
- const url = `${baseUrl}/auto-agent/clarify`;
1798
- const headers: Record<string, string> = {
1799
- "Content-Type": "application/json",
1800
- "User-Agent": "pioneer-cli/0.1.0",
1801
- };
1802
-
1803
- if (apiKey) {
1804
- headers["X-API-Key"] = apiKey;
1805
- }
1806
-
1807
- try {
1808
- const res = await fetch(url, {
1809
- method: "POST",
1810
- headers,
1811
- body: JSON.stringify(req),
1812
- });
1813
-
1814
- const text = await res.text();
1815
-
1816
- let parsedJson: unknown;
1817
- try {
1818
- parsedJson = JSON.parse(text);
1819
- } catch {
1820
- // Ignore; some responses may be SSE/event text or empty.
1821
- }
1822
-
1823
- const parseAgentStream = (raw: string): AgentChatResponse | undefined => {
1824
- const direct = parseAgentPayload(raw);
1825
- if (direct) return direct;
1826
-
1827
- const events = raw
1828
- .split("\n")
1829
- .map((line) => line.trim())
1830
- .filter((line) => line.startsWith("data:"));
1831
- const fallback: AgentChatResponse = {
1832
- answer: "",
1833
- conversation_id: "",
1834
- };
1835
-
1836
- for (const eventLine of events) {
1837
- const payload = eventLine.slice(5).trim();
1838
- if (!payload) continue;
1839
-
1840
- let event: Record<string, unknown> | undefined;
1841
- try {
1842
- event = JSON.parse(payload);
1843
- } catch {
1844
- continue;
1845
- }
1846
- if (typeof event === "object" && event) {
1847
- const type = typeof event.type === "string" ? event.type : undefined;
1848
-
1849
- if (type === "delta" && typeof event.content === "string") {
1850
- fallback.answer += event.content;
1851
- }
1852
-
1853
- if (typeof event.content === "string" && !fallback.answer) {
1854
- fallback.answer = event.content;
1855
- }
1856
-
1857
- if (typeof event.answer === "string") {
1858
- fallback.answer = event.answer;
1859
- }
1860
-
1861
- if (typeof event.conversation_id === "string") {
1862
- fallback.conversation_id = event.conversation_id;
1863
- }
1864
-
1865
- if (typeof event.tool_calls_made === "number") {
1866
- fallback.tool_calls_made = event.tool_calls_made;
1867
- }
1868
-
1869
- if (type === "complete") {
1870
- if (typeof event.conversation_id === "string") {
1871
- fallback.conversation_id = event.conversation_id;
1872
- }
1873
- if (typeof event.answer === "string") {
1874
- fallback.answer = event.answer;
1875
- }
1876
- }
1877
- }
1878
- }
1879
-
1880
- if (fallback.answer || fallback.conversation_id) {
1881
- return fallback;
1882
- }
1883
- return undefined;
1884
- };
1885
-
1886
- const parseAgentPayload = (raw: string): AgentChatResponse | undefined => {
1887
- if (!raw.trim()) {
1888
- return undefined;
1889
- }
1890
- if (typeof parsedJson !== "object" || parsedJson === null) {
1891
- return undefined;
1892
- }
1893
-
1894
- const payload = parsedJson as Record<string, unknown>;
1895
- const answer = typeof payload.answer === "string" ? payload.answer : "";
1896
- const conversationId =
1897
- typeof payload.answer === "string" && typeof payload.conversation_id === "string"
1898
- ? payload.conversation_id
1899
- : typeof payload.conversation_id === "string"
1900
- ? payload.conversation_id
1901
- : "";
1902
-
1903
- if (!answer && !conversationId && typeof payload.detail !== "string") {
1904
- return undefined;
1905
- }
1906
-
1907
- return {
1908
- answer,
1909
- conversation_id: conversationId,
1910
- ...(typeof payload.tool_calls_made === "number"
1911
- ? { tool_calls_made: payload.tool_calls_made }
1912
- : {}),
1913
- };
1914
- };
1915
-
1916
- const parsedResponse = parseAgentStream(text);
1917
-
1918
- if (!res.ok) {
1919
- const rawError = parsedJson ? JSON.stringify(parsedJson) : text || `HTTP ${res.status}`;
1920
- const extractedError = extractErrorMessage(parsedJson) || text || `HTTP ${res.status}`;
1921
- if (isAuthError(res.status, rawError)) {
1922
- return {
1923
- ok: false,
1924
- status: res.status,
1925
- error: formatAuthError(extractedError),
1926
- };
1927
- }
1928
- return {
1929
- ok: false,
1930
- status: res.status,
1931
- error: extractedError,
1932
- };
1933
- }
1934
-
1935
- if (parsedResponse) {
1936
- return { ok: true, status: res.status, data: parsedResponse };
1937
- }
1938
-
1939
- return {
1940
- ok: true,
1941
- status: res.status,
1942
- data: {
1943
- answer: text || "",
1944
- conversation_id: "",
1945
- },
1946
- };
1947
- } catch (err) {
1948
- return {
1949
- ok: false,
1950
- status: 0,
1951
- error: err instanceof Error ? err.message : String(err),
1952
- };
1953
- }
1954
- }
1955
-
1956
- // Agent session persistence APIs (Pioneer MLE agent)
1957
- export async function createAgentSession(
1958
- req: CreateChatSessionRequest
1959
- ): Promise<ApiResult<ChatSessionResponse>> {
1960
- return request<ChatSessionResponse>("POST", "/mle-agent/sessions", req);
1961
- }
1962
-
1963
- export async function getAgentSession(
1964
- sessionId: string
1965
- ): Promise<ApiResult<ChatSessionResponse>> {
1966
- return request<ChatSessionResponse>("GET", `/mle-agent/sessions/${sessionId}`);
1967
- }
1968
-
1969
- export async function listAgentSessions(): Promise<ApiResult<ChatSessionListResponse>> {
1970
- return request<ChatSessionListResponse>("GET", "/mle-agent/sessions");
1971
- }
1972
-
1973
- export async function appendSessionMessages(
1974
- sessionId: string,
1975
- req: AppendSessionMessagesRequest
1976
- ): Promise<ApiResult<{ success: boolean; session_id: string; messages_added: number }>> {
1977
- return request<{ success: boolean; session_id: string; messages_added: number }>(
1978
- "POST",
1979
- `/mle-agent/sessions/${sessionId}/messages`,
1980
- req
1981
- );
1982
- }
1983
-
1984
- export async function updateAgentSession(
1985
- sessionId: string,
1986
- req: UpdateChatSessionRequest
1987
- ): Promise<ApiResult<ChatSessionResponse>> {
1988
- return request<ChatSessionResponse>("PATCH", `/mle-agent/sessions/${sessionId}`, req);
1989
- }
1990
-
1991
- export async function deleteAgentSession(sessionId: string): Promise<ApiResult<{ success: boolean }>> {
1992
- return request<{ success: boolean }>("DELETE", `/mle-agent/sessions/${sessionId}`);
1993
- }
1994
-
1995
- export async function listAutoAgentRuns(
1996
- limit = 20,
1997
- offset = 0
1998
- ): Promise<ApiResult<{ runs: AutoAgentRun[] }>> {
1999
- return request<{ runs: AutoAgentRun[] }>(
2000
- "GET",
2001
- `/auto-agent/runs?limit=${limit}&offset=${offset}`
2002
- );
2003
- }
2004
-
2005
- export async function getAutoAgentRun(
2006
- runId: string
2007
- ): Promise<ApiResult<AutoAgentRun>> {
2008
- return request<AutoAgentRun>("GET", `/auto-agent/run/${runId}`);
2009
- }
2010
-
2011
- export async function stopAutoAgentRun(
2012
- runId: string
2013
- ): Promise<ApiResult<{ success: boolean }>> {
2014
- return request<{ success: boolean }>("POST", `/auto-agent/run/${runId}/stop`);
2015
- }
2016
-
2017
- // ─────────────────────────────────────────────────────────────────────────────
2018
- // Decoder Generation
2019
- // ─────────────────────────────────────────────────────────────────────────────
2020
-
2021
- export interface GenerateDecoderRequest {
2022
- domain_description: string;
2023
- instruction?: string;
2024
- num_examples?: number;
2025
- temperature?: number;
2026
- generation_profile?: "auto" | "fast" | "balanced" | "quality";
2027
- quality?: "light" | "medium" | "heavy";
2028
- include_reasoning_trace?: boolean;
2029
- reasoning_effort?: "low" | "medium" | "high";
2030
- constraints?: ConstraintRequest[];
2031
- save_dataset?: boolean;
2032
- dataset_name?: string;
2033
- project_id?: string;
2034
- use_meta_felix?: boolean;
2035
- session_id?: string;
2036
- config_num_examples?: number;
2037
- min_criteria?: number;
2038
- target_choices?: number;
2039
- split_ratio?: Record<string, number>;
2040
- negative_ratio?: number;
2041
- multiplicator?: Record<string, unknown>;
2042
- type?: "training" | "evaluation" | "split";
2043
- visibility?: "private" | "public";
2044
- }
2045
-
2046
- export async function generateDecoder(
2047
- req: GenerateDecoderRequest
2048
- ): Promise<ApiResult<GenerateJobStatus>> {
2049
- return generateDataset({
2050
- task_type: "decoder",
2051
- dataset_name: req.dataset_name ?? makeDefaultDatasetName("decoder"),
2052
- domain_description: req.domain_description,
2053
- instruction: req.instruction,
2054
- num_examples: req.num_examples,
2055
- temperature: req.temperature,
2056
- generation_profile: req.generation_profile,
2057
- quality: req.quality,
2058
- constraints: req.constraints,
2059
- project_id: req.project_id,
2060
- include_reasoning_trace: req.include_reasoning_trace,
2061
- reasoning_effort: req.reasoning_effort,
2062
- use_meta_felix: req.use_meta_felix,
2063
- session_id: req.session_id,
2064
- config_num_examples: req.config_num_examples,
2065
- min_criteria: req.min_criteria,
2066
- target_choices: req.target_choices,
2067
- split_ratio: req.split_ratio,
2068
- negative_ratio: req.negative_ratio,
2069
- multiplicator: req.multiplicator,
2070
- type: req.type,
2071
- visibility: req.visibility,
2072
- });
2073
- }
2074
-
2075
- // ─────────────────────────────────────────────────────────────────────────────
2076
- // Evaluations
2077
- // ─────────────────────────────────────────────────────────────────────────────
2078
-
2079
- export interface BaselineModel {
2080
- id: string;
2081
- name: string;
2082
- provider: string;
2083
- description: string;
2084
- }
2085
-
2086
- export interface BaselineModelsResponse {
2087
- success: boolean;
2088
- models: BaselineModel[];
2089
- count: number;
2090
- }
2091
-
2092
- export async function listBaselineModels(): Promise<ApiResult<BaselineModelsResponse>> {
2093
- return request<BaselineModelsResponse>("GET", "/felix/baseline-models");
2094
- }
2095
-
2096
- export interface Evaluation {
2097
- id: string;
2098
- user_id: string;
2099
- project_id?: string;
2100
- model_id: string;
2101
- dataset_name: string;
2102
- dataset_version: string;
2103
- provider?: string;
2104
- model_name?: string;
2105
- f1_score?: number;
2106
- precision_score?: number;
2107
- recall_score?: number;
2108
- accuracy?: number;
2109
- validation_loss?: number;
2110
- total_tokens?: number;
2111
- total_cost_usd?: number;
2112
- total_latency_ms?: number;
2113
- max_examples?: number;
2114
- seed?: number;
2115
- sample_count?: number;
2116
- evaluation_time_ms?: number;
2117
- status: string;
2118
- job_reference?: string;
2119
- error_message?: string;
2120
- created_at: string;
2121
- completed_at?: string;
2122
- }
2123
-
2124
- export interface EvaluationListResponse {
2125
- success: boolean;
2126
- evaluations: Evaluation[];
2127
- count: number;
2128
- }
2129
-
2130
- export async function listEvaluations(): Promise<
2131
- ApiResult<EvaluationListResponse>
2132
- > {
2133
- return request<EvaluationListResponse>("GET", "/felix/evaluations");
2134
- }
2135
-
2136
- export async function getEvaluation(
2137
- evalId: string
2138
- ): Promise<ApiResult<Evaluation>> {
2139
- return request<Evaluation>("GET", `/felix/evaluations/${evalId}`);
2140
- }
2141
-
2142
- export interface EvaluationCreateRequest {
2143
- dataset: DatasetRef;
2144
- model_id: string;
2145
- base_model?: string;
2146
- comparison_models?: string[];
2147
- dataset_names?: string[];
2148
- provider?: "felix" | "openai" | "together";
2149
- max_examples?: number;
2150
- seed?: number;
2151
- task_type?: string;
2152
- text_column?: string;
2153
- label_column?: string;
2154
- project_id?: string;
2155
- }
2156
-
2157
- export interface EvaluationCreateResponse {
2158
- success: boolean;
2159
- evaluations: Evaluation[];
2160
- count: number;
2161
- }
2162
-
2163
- export async function createEvaluation(
2164
- req: EvaluationCreateRequest
2165
- ): Promise<ApiResult<EvaluationCreateResponse>> {
2166
- const { dataset, ...rest } = req;
2167
- const baseModel = req.base_model ?? req.model_id;
2168
- return request<EvaluationCreateResponse>("POST", "/felix/evaluations", {
2169
- ...rest,
2170
- base_model: baseModel,
2171
- dataset_name: dataset.name,
2172
- dataset_version: dataset.version,
2173
- });
2174
- }
2175
-
2176
- export interface ModelWithEvaluation {
2177
- model_id: string;
2178
- model_name: string;
2179
- is_base_model: boolean;
2180
- evaluation?: Evaluation;
2181
- }
2182
-
2183
- export interface DatasetEvaluationsResponse {
2184
- success: boolean;
2185
- dataset_name: string;
2186
- dataset_version: string;
2187
- sample_count: number;
2188
- models: ModelWithEvaluation[];
2189
- count: number;
2190
- }
2191
-
2192
- export async function getDatasetEvaluations(
2193
- dataset: DatasetRef
2194
- ): Promise<ApiResult<DatasetEvaluationsResponse>> {
2195
- return request<DatasetEvaluationsResponse>(
2196
- "GET",
2197
- `/felix/datasets/${dataset.name}/${dataset.version}/evaluations`
2198
- );
2199
- }
2200
-
2201
- export interface DeleteEvaluationResponse {
2202
- success: boolean;
2203
- message: string;
2204
- }
2205
-
2206
- export async function deleteEvaluation(
2207
- evaluationId: string
2208
- ): Promise<ApiResult<DeleteEvaluationResponse>> {
2209
- return request<DeleteEvaluationResponse>("DELETE", `/felix/evaluations/${evaluationId}`);
2210
- }
2211
-
2212
- export interface UpdateEvaluationProjectRequest {
2213
- evaluation_id: string;
2214
- project_id?: string;
2215
- }
2216
-
2217
- export interface UpdateEvaluationProjectResponse {
2218
- success: boolean;
2219
- message: string;
2220
- }
2221
-
2222
- export async function updateEvaluationProject(
2223
- req: UpdateEvaluationProjectRequest
2224
- ): Promise<ApiResult<UpdateEvaluationProjectResponse>> {
2225
- return request<UpdateEvaluationProjectResponse>(
2226
- "PATCH",
2227
- `/felix/evaluations/${req.evaluation_id}/project`,
2228
- { project_id: req.project_id }
2229
- );
2230
- }
2231
-
2232
- // ─────────────────────────────────────────────────────────────────────────────
2233
- // Benchmarks
2234
- // ─────────────────────────────────────────────────────────────────────────────
2235
-
2236
- export interface BenchmarkInfo {
2237
- name: string;
2238
- description: string;
2239
- task: string;
2240
- dataset_source?: string;
2241
- metrics: string[];
2242
- }
2243
-
2244
- export interface ListBenchmarksResponse {
2245
- success: boolean;
2246
- benchmarks: Record<string, BenchmarkInfo[]>;
2247
- count: number;
2248
- }
2249
-
2250
- export async function listBenchmarks(): Promise<ApiResult<ListBenchmarksResponse>> {
2251
- return request<ListBenchmarksResponse>("GET", "/felix/benchmarks");
2252
- }
2253
-
2254
- export interface BenchmarkEvaluationRequest {
2255
- model_id: string;
2256
- task: "ner" | "text_classification";
2257
- benchmark: string;
2258
- max_samples?: number;
2259
- split?: string;
2260
- benchmark_config?: Record<string, unknown>;
2261
- }
2262
-
2263
- export interface BenchmarkEvaluationResponse {
2264
- success: boolean;
2265
- evaluation_id: string;
2266
- status: "pending" | "running" | "complete" | "errored";
2267
- task: string;
2268
- benchmark: string;
2269
- model_id: string;
2270
- metrics?: Record<string, unknown>;
2271
- error_message?: string;
2272
- created_at: string;
2273
- completed_at?: string;
2274
- }
2275
-
2276
- export async function startBenchmarkEvaluation(
2277
- req: BenchmarkEvaluationRequest
2278
- ): Promise<ApiResult<BenchmarkEvaluationResponse>> {
2279
- return request<BenchmarkEvaluationResponse>("POST", "/felix/benchmarks/evaluate", req);
2280
- }
2281
-
2282
- export async function getBenchmarkEvaluation(
2283
- evaluationId: string
2284
- ): Promise<ApiResult<BenchmarkEvaluationResponse>> {
2285
- return request<BenchmarkEvaluationResponse>(
2286
- "GET",
2287
- `/felix/benchmarks/evaluate/${evaluationId}`
2288
- );
2289
- }
2290
-
2291
- export async function cancelBenchmarkEvaluation(
2292
- evaluationId: string
2293
- ): Promise<ApiResult<{ success: boolean; message: string }>> {
2294
- return request<{ success: boolean; message: string }>(
2295
- "POST",
2296
- `/felix/benchmarks/evaluate/${evaluationId}/cancel`
2297
- );
2298
- }
2299
-
2300
- // ─────────────────────────────────────────────────────────────────────────────
2301
- // Data Editing (operates on persisted datasets)
2302
- // ─────────────────────────────────────────────────────────────────────────────
2303
-
2304
- export interface PIIFinding {
2305
- row_index: number;
2306
- column: string;
2307
- entity_type: string;
2308
- text: string;
2309
- start: number;
2310
- end: number;
2311
- score: number;
2312
- }
2313
-
2314
- export interface DataEditingScanRequest {
2315
- dataset: DatasetRef;
2316
- columns?: string[];
2317
- threshold?: number;
2318
- }
2319
-
2320
- export interface DataEditingScanResponse {
2321
- success: boolean;
2322
- dataset_id: string;
2323
- scan_type: "pii" | "phd";
2324
- findings_count: number;
2325
- affected_rows: number;
2326
- findings: PIIFinding[];
2327
- }
2328
-
2329
- export async function scanForPII(
2330
- req: DataEditingScanRequest
2331
- ): Promise<ApiResult<DataEditingScanResponse>> {
2332
- const { dataset, ...rest } = req;
2333
- return request<DataEditingScanResponse>("POST", "/felix/data-editing/scan-pii", {
2334
- ...rest,
2335
- dataset_name: dataset.name,
2336
- dataset_version: dataset.version,
2337
- });
2338
- }
2339
-
2340
- export async function scanForPHD(
2341
- req: DataEditingScanRequest
2342
- ): Promise<ApiResult<DataEditingScanResponse>> {
2343
- const { dataset, ...rest } = req;
2344
- return request<DataEditingScanResponse>("POST", "/felix/data-editing/scan-phd", {
2345
- ...rest,
2346
- dataset_name: dataset.name,
2347
- dataset_version: dataset.version,
2348
- });
2349
- }
2350
-
2351
- export interface DataEditingDismissOutlierRequest {
2352
- dataset: DatasetRef;
2353
- fingerprint: string;
2354
- }
2355
-
2356
- export interface DataEditingDismissOutlierResponse {
2357
- success: boolean;
2358
- }
2359
-
2360
- export async function dismissOutlier(
2361
- req: DataEditingDismissOutlierRequest
2362
- ): Promise<ApiResult<DataEditingDismissOutlierResponse>> {
2363
- const { dataset, ...rest } = req;
2364
- return request<DataEditingDismissOutlierResponse>("POST", "/felix/dataset/outliers/dismiss", {
2365
- ...rest,
2366
- dataset_name: dataset.name,
2367
- dataset_version: dataset.version,
2368
- });
2369
- }
2370
-
2371
- export interface DataEditingRemoveRequest {
2372
- dataset: DatasetRef;
2373
- findings: PIIFinding[];
2374
- redaction_method?: "redact" | "remove_row" | "mask";
2375
- save_as_new?: boolean;
2376
- }
2377
-
2378
- export interface DataEditingRemoveResponse {
2379
- success: boolean;
2380
- dataset_id: string;
2381
- new_dataset_id?: string;
2382
- rows_affected: number;
2383
- entities_removed: number;
2384
- message: string;
2385
- }
2386
-
2387
- export async function removePII(
2388
- req: DataEditingRemoveRequest
2389
- ): Promise<ApiResult<DataEditingRemoveResponse>> {
2390
- const { dataset, ...rest } = req;
2391
- return request<DataEditingRemoveResponse>("POST", "/felix/data-editing/remove-pii", {
2392
- ...rest,
2393
- dataset_name: dataset.name,
2394
- dataset_version: dataset.version,
2395
- });
2396
- }
2397
-
2398
- export interface DataEditingSubsampleRequest {
2399
- dataset: DatasetRef;
2400
- method?: "random" | "balanced" | "stratified";
2401
- n: number;
2402
- label_column?: string;
2403
- seed?: number;
2404
- save_as_new?: boolean;
2405
- }
2406
-
2407
- export interface DataEditingSubsampleResponse {
2408
- success: boolean;
2409
- dataset_id: string;
2410
- new_dataset_id: string;
2411
- original_rows: number;
2412
- new_rows: number;
2413
- method: string;
2414
- message: string;
2415
- }
2416
-
2417
- export async function subsampleDataset(
2418
- req: DataEditingSubsampleRequest
2419
- ): Promise<ApiResult<DataEditingSubsampleResponse>> {
2420
- const { dataset, ...rest } = req;
2421
- return request<DataEditingSubsampleResponse>("POST", "/felix/data-editing/subsample", {
2422
- ...rest,
2423
- dataset_name: dataset.name,
2424
- dataset_version: dataset.version,
2425
- });
2426
- }
2427
-
2428
- export interface DataEditingCheckLabelsRequest {
2429
- dataset: DatasetRef;
2430
- text_column: string;
2431
- label_column: string;
2432
- sample_size?: number;
2433
- }
2434
-
2435
- export interface LabelCheckResult {
2436
- row_index: number;
2437
- text: string;
2438
- current_label: string;
2439
- suggested_label: string;
2440
- confidence: number;
2441
- reasoning: string;
2442
- }
2443
-
2444
- export interface DataEditingCheckLabelsResponse {
2445
- success: boolean;
2446
- dataset_id: string;
2447
- checked_count: number;
2448
- issues_found: number;
2449
- results: LabelCheckResult[];
2450
- }
2451
-
2452
- export async function checkLabels(
2453
- req: DataEditingCheckLabelsRequest
2454
- ): Promise<ApiResult<DataEditingCheckLabelsResponse>> {
2455
- const { dataset, ...rest } = req;
2456
- return request<DataEditingCheckLabelsResponse>("POST", "/felix/data-editing/check-labels", {
2457
- ...rest,
2458
- dataset_name: dataset.name,
2459
- dataset_version: dataset.version,
2460
- });
2461
- }
2462
-
2463
- // ─────────────────────────────────────────────────────────────────────────────
2464
- // Hugging Face Integration
2465
- // ─────────────────────────────────────────────────────────────────────────────
2466
-
2467
- export interface HuggingFacePushRequest {
2468
- hf_token: string;
2469
- repo_id: string;
2470
- private?: boolean;
2471
- commit_message?: string;
2472
- }
2473
-
2474
- export interface HuggingFacePushResponse {
2475
- success: boolean;
2476
- url: string;
2477
- repo_id: string;
2478
- version: string;
2479
- message: string;
2480
- }
2481
-
2482
- export async function pushDatasetToHub(
2483
- dataset: DatasetRef,
2484
- options: HuggingFacePushRequest
2485
- ): Promise<ApiResult<HuggingFacePushResponse>> {
2486
- return request<HuggingFacePushResponse>(
2487
- "POST",
2488
- `/felix/datasets/${dataset.name}/${dataset.version}/push-to-hub`,
2489
- options
2490
- );
2491
- }
2492
-
2493
- export interface HuggingFacePushModelRequest {
2494
- hf_token: string;
2495
- repo_id: string;
2496
- private?: boolean;
2497
- commit_message?: string;
2498
- }
2499
-
2500
- export interface HuggingFacePushModelResponse {
2501
- success: boolean;
2502
- url: string;
2503
- repo_id: string;
2504
- job_id: string;
2505
- message: string;
2506
- }
2507
-
2508
- export async function pushModelToHub(
2509
- jobId: string,
2510
- options: HuggingFacePushModelRequest
2511
- ): Promise<ApiResult<HuggingFacePushModelResponse>> {
2512
- return request<HuggingFacePushModelResponse>(
2513
- "POST",
2514
- `/felix/training-jobs/${jobId}/push-to-hub`,
2515
- options
2516
- );
2517
- }
2518
-
2519
- export interface HuggingFacePullRequest {
2520
- repo_id: string;
2521
- hf_token: string;
2522
- revision?: string;
2523
- name?: string;
2524
- }
2525
-
2526
- export async function pullDatasetFromHub(
2527
- options: HuggingFacePullRequest
2528
- ): Promise<ApiResult<Dataset>> {
2529
- return request<Dataset>(
2530
- "POST",
2531
- "/felix/datasets/pull-from-hub",
2532
- options
2533
- );
2534
- }
2535
-
2536
- // ─────────────────────────────────────────────────────────────────────────────
2537
- // Constraints Generation
2538
- // ─────────────────────────────────────────────────────────────────────────────
2539
-
2540
- export interface GenerateNERConstraintsRequest {
2541
- labels: string[];
2542
- domain_description?: string;
2543
- min_criteria?: number;
2544
- }
2545
-
2546
- export interface GenerateClassificationConstraintsRequest {
2547
- labels: string[];
2548
- domain_description?: string;
2549
- min_criteria?: number;
2550
- }
2551
-
2552
- export interface GenerateRecordsConstraintsRequest {
2553
- fields: RecordField[];
2554
- domain_description?: string;
2555
- min_criteria?: number;
2556
- }
2557
-
2558
- export interface GenerateConstraintsResponse {
2559
- success: boolean;
2560
- constraints: ConstraintRequest[];
2561
- count: number;
2562
- token_usage?: number;
2563
- }
2564
-
2565
- export async function generateNERConstraints(
2566
- req: GenerateNERConstraintsRequest
2567
- ): Promise<ApiResult<GenerateConstraintsResponse>> {
2568
- return request<GenerateConstraintsResponse>(
2569
- "POST",
2570
- "/felix/constraints/ner",
2571
- req
2572
- );
2573
- }
2574
-
2575
- export async function generateClassificationConstraints(
2576
- req: GenerateClassificationConstraintsRequest
2577
- ): Promise<ApiResult<GenerateConstraintsResponse>> {
2578
- return request<GenerateConstraintsResponse>(
2579
- "POST",
2580
- "/felix/constraints/classification",
2581
- req
2582
- );
2583
- }
2584
-
2585
- export async function generateRecordsConstraints(
2586
- req: GenerateRecordsConstraintsRequest
2587
- ): Promise<ApiResult<GenerateConstraintsResponse>> {
2588
- return request<GenerateConstraintsResponse>(
2589
- "POST",
2590
- "/felix/constraints/records",
2591
- req
2592
- );
2593
- }
2594
-
2595
- export interface ExpandConstraintChoicesRequest {
2596
- constraint: ConstraintRequest;
2597
- task_description: string;
2598
- max_count?: number;
2599
- }
2600
-
2601
- export interface ExpandConstraintChoicesResponse {
2602
- success: boolean;
2603
- constraint: ConstraintRequest;
2604
- expanded_choices: string[];
2605
- count: number;
2606
- token_usage?: number;
2607
- }
2608
-
2609
- export async function expandConstraintChoices(
2610
- req: ExpandConstraintChoicesRequest
2611
- ): Promise<ApiResult<ExpandConstraintChoicesResponse>> {
2612
- return request<ExpandConstraintChoicesResponse>(
2613
- "POST",
2614
- "/felix/constraints/expand",
2615
- req
2616
- );
2617
- }
2618
-
2619
- // ─────────────────────────────────────────────────────────────────────────────
2620
- // Dataset Augmentation
2621
- // ─────────────────────────────────────────────────────────────────────────────
2622
-
2623
- export interface AugmentationOperation {
2624
- type: "remove_duplicates" | "remove_outliers" | "balance";
2625
- enabled: boolean;
2626
- }
2627
-
2628
- export interface DatasetAugmentationRequest {
2629
- task_type: "ner" | "classification";
2630
- operations: AugmentationOperation[];
2631
- new_dataset_name: string;
2632
- dataset_name?: string;
2633
- dataset_version?: string;
2634
- dataset?: Record<string, unknown>[];
2635
- target_distribution?: Record<string, number>;
2636
- domain_description?: string;
2637
- labels?: string[];
2638
- }
2639
-
2640
- export interface DatasetAugmentationResponse {
2641
- success: boolean;
2642
- original_dataset_name?: string;
2643
- original_dataset_version?: string;
2644
- new_dataset: Dataset;
2645
- modifications: Record<string, unknown>;
2646
- distribution_comparison: Record<string, unknown>[];
2647
- message: string;
2648
- }
2649
-
2650
- export async function augmentDataset(
2651
- req: DatasetAugmentationRequest
2652
- ): Promise<ApiResult<DatasetAugmentationResponse>> {
2653
- return request<DatasetAugmentationResponse>(
2654
- "POST",
2655
- "/felix/dataset/augment",
2656
- req
2657
- );
2658
- }
2659
-
2660
- // ─────────────────────────────────────────────────────────────────────────────
2661
- // Dataset Query
2662
- // ─────────────────────────────────────────────────────────────────────────────
2663
-
2664
- export interface DatasetQueryRequest {
2665
- dataset_name: string;
2666
- code: string;
2667
- version?: string;
2668
- timeout?: number;
2669
- max_rows_returned?: number;
2670
- }
2671
-
2672
- export interface DatasetQueryResponse {
2673
- success: boolean;
2674
- dataset_name: string;
2675
- dataset_version: string;
2676
- result: unknown;
2677
- result_type: "dataframe" | "series" | "scalar" | "string" | "none";
2678
- row_count?: number;
2679
- columns?: string[];
2680
- execution_time_ms: number;
2681
- truncated: boolean;
2682
- message: string;
2683
- }
2684
-
2685
- export async function queryDataset(
2686
- req: DatasetQueryRequest
2687
- ): Promise<ApiResult<DatasetQueryResponse>> {
2688
- return request<DatasetQueryResponse>("POST", "/felix/dataset/query", req);
2689
- }
2690
-
2691
- // ─────────────────────────────────────────────────────────────────────────────
2692
- // Dataset Grow
2693
- // ─────────────────────────────────────────────────────────────────────────────
2694
-
2695
- export interface GrowDatasetRequest {
2696
- dataset_id: string;
2697
- new_dataset_name: string;
2698
- target_size: number;
2699
- class_balance?: boolean;
2700
- domain_description?: string;
2701
- temperature?: number;
2702
- session_id?: string;
2703
- }
2704
-
2705
- export interface GrowDatasetResponse {
2706
- success: boolean;
2707
- dataset: Record<string, unknown>;
2708
- original_size: number;
2709
- new_size: number;
2710
- generated_count: number;
2711
- distribution: Record<string, number>;
2712
- token_usage?: number;
2713
- }
2714
-
2715
- export async function growDataset(
2716
- req: GrowDatasetRequest
2717
- ): Promise<ApiResult<GrowDatasetResponse>> {
2718
- return request<GrowDatasetResponse>("POST", "/felix/dataset/grow", req);
2719
- }
2720
-
2721
- // ─────────────────────────────────────────────────────────────────────────────
2722
- // Embeddings
2723
- // ─────────────────────────────────────────────────────────────────────────────
2724
-
2725
- export interface EmbeddingsRequest {
2726
- text: string;
2727
- model?: string;
2728
- }
2729
-
2730
- export interface EmbeddingsResponse {
2731
- embedding: number[];
2732
- }
2733
-
2734
- export async function createEmbeddings(
2735
- req: EmbeddingsRequest
2736
- ): Promise<ApiResult<EmbeddingsResponse>> {
2737
- return request<EmbeddingsResponse>("POST", "/felix/embeddings", req);
2738
- }
2739
-
2740
- // ─────────────────────────────────────────────────────────────────────────────
2741
- // Activity
2742
- // ─────────────────────────────────────────────────────────────────────────────
2743
-
2744
- export interface ActivityEvent {
2745
- id: string;
2746
- type: "project" | "dataset" | "model" | "evaluation" | "notebook" | "deployment";
2747
- name: string;
2748
- item_id: string;
2749
- created_by: string;
2750
- created_at: string;
2751
- project_id?: string;
2752
- }
2753
-
2754
- export interface ActivityLogResponse {
2755
- success: boolean;
2756
- events?: ActivityEvent[];
2757
- count: number;
2758
- }
2759
-
2760
- export async function listActivity(
2761
- options: { limit?: number } = {}
2762
- ): Promise<ApiResult<ActivityLogResponse>> {
2763
- const params = new URLSearchParams();
2764
- if (options.limit) params.set("limit", String(options.limit));
2765
- const query = params.toString();
2766
- const url = query ? `/felix/activity?${query}` : "/felix/activity";
2767
- return request<ActivityLogResponse>("GET", url);
2768
- }
2769
-
2770
- // ─────────────────────────────────────────────────────────────────────────────
2771
- // Projects
2772
- // ─────────────────────────────────────────────────────────────────────────────
2773
-
2774
- export interface ProjectCreateRequest {
2775
- name?: string;
2776
- icon?: string;
2777
- repo?: string;
2778
- description?: string;
2779
- active_model_id?: string;
2780
- selected_model_id?: string;
2781
- example?: Record<string, unknown>;
2782
- }
2783
-
2784
- export interface ProjectUpdateRequest {
2785
- name?: string;
2786
- icon?: string;
2787
- repo?: string;
2788
- description?: string;
2789
- selected_model_id?: string;
2790
- }
2791
-
2792
- export interface ProjectResponse {
2793
- id: string;
2794
- user_id: string;
2795
- name: string;
2796
- icon?: string;
2797
- repo?: string;
2798
- description?: string;
2799
- selected_model_id?: string;
2800
- example?: Record<string, unknown>;
2801
- created_at: string;
2802
- updated_at: string;
2803
- }
2804
-
2805
- export interface ProjectListResponse {
2806
- success: boolean;
2807
- projects: ProjectResponse[];
2808
- count: number;
2809
- }
2810
-
2811
- export interface ProjectDeleteResponse {
2812
- success: boolean;
2813
- message: string;
2814
- project_id: string;
2815
- }
2816
-
2817
- export interface ProjectDeploymentCreate {
2818
- training_job_id: string;
2819
- reason?: string;
2820
- }
2821
-
2822
- export interface ProjectDeploymentResponse {
2823
- success: boolean;
2824
- message?: string;
2825
- project_id?: string;
2826
- training_job_id?: string;
2827
- deployment_id?: string;
2828
- }
2829
-
2830
- export interface ProjectDatasetCountResponse {
2831
- project_id: string;
2832
- dataset_count: number;
2833
- can_delete: boolean;
2834
- }
2835
-
2836
- export interface QualityMetricsResponse {
2837
- project_id: string;
2838
- pass_count: number;
2839
- fail_count: number;
2840
- uncertain_count: number;
2841
- total_judged: number;
2842
- pass_rate: number;
2843
- fail_rate: number;
2844
- }
2845
-
2846
- export async function listProjects(): Promise<ApiResult<ProjectListResponse>> {
2847
- return request<ProjectListResponse>("GET", "/projects");
2848
- }
2849
-
2850
- export async function createProject(
2851
- req: ProjectCreateRequest
2852
- ): Promise<ApiResult<ProjectResponse>> {
2853
- return request<ProjectResponse>("POST", "/projects", req);
2854
- }
2855
-
2856
- export async function getProject(projectId: string): Promise<ApiResult<ProjectResponse>> {
2857
- return request<ProjectResponse>("GET", `/projects/${projectId}`);
2858
- }
2859
-
2860
- export async function updateProject(
2861
- projectId: string,
2862
- req: ProjectUpdateRequest
2863
- ): Promise<ApiResult<ProjectResponse>> {
2864
- return request<ProjectResponse>("PATCH", `/projects/${projectId}`, req);
2865
- }
2866
-
2867
- export async function deleteProject(
2868
- projectId: string
2869
- ): Promise<ApiResult<ProjectDeleteResponse>> {
2870
- return request<ProjectDeleteResponse>("DELETE", `/projects/${projectId}`);
2871
- }
2872
-
2873
- export async function getProjectDatasetCount(
2874
- projectId: string
2875
- ): Promise<ApiResult<ProjectDatasetCountResponse>> {
2876
- return request<ProjectDatasetCountResponse>(
2877
- "GET",
2878
- `/projects/${projectId}/dataset-count`
2879
- );
2880
- }
2881
-
2882
- export async function getProjectQualityMetrics(
2883
- projectId: string
2884
- ): Promise<ApiResult<QualityMetricsResponse>> {
2885
- return request<QualityMetricsResponse>(
2886
- "GET",
2887
- `/projects/${projectId}/quality-metrics`
2888
- );
2889
- }
2890
-
2891
- export async function deployTrainingJobToProject(
2892
- projectId: string,
2893
- req: ProjectDeploymentCreate
2894
- ): Promise<ApiResult<ProjectDeploymentResponse>> {
2895
- return request<ProjectDeploymentResponse>(
2896
- "POST",
2897
- `/projects/${projectId}/deployments`,
2898
- req
2899
- );
2900
- }
2901
-
2902
- export async function rollbackProjectDeployment(
2903
- projectId: string,
2904
- deploymentId: string
2905
- ): Promise<ApiResult<ProjectDeploymentResponse>> {
2906
- return request<ProjectDeploymentResponse>(
2907
- "POST",
2908
- `/projects/${projectId}/deployments/${deploymentId}/rollback`
2909
- );
2910
- }
2911
-
2912
- // ─────────────────────────────────────────────────────────────────────────────
2913
- // Presets
2914
- // ─────────────────────────────────────────────────────────────────────────────
2915
-
2916
- export interface PresetMetadata {
2917
- id: string;
2918
- name: string;
2919
- description: string;
2920
- task_type: string;
2921
- tags?: string[];
2922
- }
2923
-
2924
- export interface PresetDetail {
2925
- id: string;
2926
- name: string;
2927
- description: string;
2928
- task_type: string;
2929
- config: Record<string, unknown>;
2930
- tags?: string[];
2931
- }
2932
-
2933
- export async function listPresets(): Promise<ApiResult<PresetMetadata[]>> {
2934
- return request<PresetMetadata[]>("GET", "/felix/presets");
2935
- }
2936
-
2937
- export async function getPreset(
2938
- presetId: string
2939
- ): Promise<ApiResult<PresetDetail>> {
2940
- return request<PresetDetail>("GET", `/felix/presets/${presetId}`);
2941
- }
2942
-
2943
- // ─────────────────────────────────────────────────────────────────────────────
2944
- // Adaptive Annotation
2945
- // ─────────────────────────────────────────────────────────────────────────────
2946
-
2947
- export interface StartAnnotationSessionRequest {
2948
- column_name: string;
2949
- input_columns: string[];
2950
- task_description: string;
2951
- task_type?: "classification" | "scoring" | "entity_extraction" | "json_extraction";
2952
- output_dataset_name?: string;
2953
- possible_labels?: Array<{ name: string; description?: string }>;
2954
- scoring_criteria?: Array<Record<string, unknown>>;
2955
- entity_types?: Array<{ name: string; description?: string }>;
2956
- json_schema?: Array<Record<string, unknown>>;
2957
- auto_accept_threshold?: number;
2958
- }
2959
-
2960
- export interface StartAnnotationSessionResponse {
2961
- success: boolean;
2962
- session_id: string;
2963
- dataset_id: string;
2964
- task_type: string;
2965
- total_items: number;
2966
- }
2967
-
2968
- export async function startAnnotationSession(
2969
- datasetId: string,
2970
- req: StartAnnotationSessionRequest
2971
- ): Promise<ApiResult<StartAnnotationSessionResponse>> {
2972
- return request<StartAnnotationSessionResponse>(
2973
- "POST",
2974
- `/felix/datasets/annotate/${datasetId}/start`,
2975
- req
2976
- );
2977
- }
2978
-
2979
- export interface NextAnnotationItemResponse {
2980
- complete: boolean;
2981
- rate_limited?: boolean;
2982
- auto_accepted_in_call?: number;
2983
- item_id?: number;
2984
- item_data?: Record<string, unknown>;
2985
- task_type?: string;
2986
- task_description?: string;
2987
- possible_labels?: string[];
2988
- scoring_criteria?: Array<Record<string, unknown>>;
2989
- entity_types?: Array<Record<string, unknown>>;
2990
- json_schema?: Array<Record<string, unknown>>;
2991
- suggestion?: Record<string, unknown>;
2992
- stats?: Record<string, unknown>;
2993
- }
2994
-
2995
- export async function getNextAnnotationItem(
2996
- datasetId: string,
2997
- options: { maxAutoAccept?: number } = {}
2998
- ): Promise<ApiResult<NextAnnotationItemResponse>> {
2999
- const params = new URLSearchParams();
3000
- if (options.maxAutoAccept !== undefined) {
3001
- params.set("max_auto_accept", String(options.maxAutoAccept));
3002
- }
3003
- const query = params.toString();
3004
- const url = `/felix/datasets/annotate/${datasetId}/next${query ? `?${query}` : ""}`;
3005
- return request<NextAnnotationItemResponse>("GET", url);
3006
- }
3007
-
3008
- export interface AnnotationFeedbackRequest {
3009
- item_id: number;
3010
- thumb: "up" | "down";
3011
- corrected_label?: string;
3012
- corrected_criteria?: Record<string, string>;
3013
- corrected_entities?: string[][];
3014
- corrected_json?: Record<string, unknown>;
3015
- }
3016
-
3017
- export interface AnnotationFeedbackResponse {
3018
- success: boolean;
3019
- item_id: number;
3020
- action: "up" | "down";
3021
- was_correct: boolean;
3022
- similar_items_queued?: number;
3023
- stats?: Record<string, unknown>;
3024
- }
3025
-
3026
- export async function submitAnnotationFeedback(
3027
- datasetId: string,
3028
- req: AnnotationFeedbackRequest
3029
- ): Promise<ApiResult<AnnotationFeedbackResponse>> {
3030
- return request<AnnotationFeedbackResponse>(
3031
- "POST",
3032
- `/felix/datasets/annotate/${datasetId}/feedback`,
3033
- req
3034
- );
3035
- }
3036
-
3037
- export interface AnnotationStatsResponse {
3038
- total_items: number;
3039
- annotated: number;
3040
- remaining: number;
3041
- auto_accepted: number;
3042
- human_annotated: number;
3043
- auto_accept_rate: number;
3044
- label_stats?: Record<string, unknown>;
3045
- }
3046
-
3047
- export async function getAnnotationStats(
3048
- datasetId: string
3049
- ): Promise<ApiResult<AnnotationStatsResponse>> {
3050
- return request<AnnotationStatsResponse>(
3051
- "GET",
3052
- `/felix/datasets/annotate/${datasetId}/stats`
3053
- );
3054
- }
3055
-
3056
- export interface AnnotationDownloadResponse {
3057
- data: Record<string, unknown>[];
3058
- columns: string[];
3059
- rows: number;
3060
- stats?: AnnotationStatsResponse;
3061
- }
3062
-
3063
- export async function downloadAnnotatedData(
3064
- datasetId: string
3065
- ): Promise<ApiResult<AnnotationDownloadResponse>> {
3066
- return request<AnnotationDownloadResponse>(
3067
- "GET",
3068
- `/felix/datasets/annotate/${datasetId}/download`
3069
- );
3070
- }
3071
-
3072
- export interface EndAnnotationSessionResponse {
3073
- success: boolean;
3074
- message: string;
3075
- final_stats?: AnnotationStatsResponse;
3076
- }
3077
-
3078
- export async function endAnnotationSession(
3079
- datasetId: string
3080
- ): Promise<ApiResult<EndAnnotationSessionResponse>> {
3081
- return request<EndAnnotationSessionResponse>(
3082
- "DELETE",
3083
- `/felix/datasets/annotate/${datasetId}/session`
3084
- );
3085
- }
3086
-