roe-typescript 0.1.1 → 0.1.3

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.
@@ -20,11 +20,30 @@ export class Job {
20
20
  while (true) {
21
21
  const status = await this.retrieveStatus();
22
22
  const code = status.status;
23
- if (code === JobStatus.SUCCESS || code === JobStatus.CACHED) {
24
- return this.retrieveResult();
25
- }
26
- if (code === JobStatus.FAILURE || code === JobStatus.CANCELLED) {
27
- throw new Error(`Job ${this.jobId} failed with status ${code}${status.error_message ? `: ${status.error_message}` : ""}`);
23
+ if (code === JobStatus.SUCCESS ||
24
+ code === JobStatus.CACHED ||
25
+ code === JobStatus.FAILURE ||
26
+ code === JobStatus.CANCELLED) {
27
+ const isFailed = code === JobStatus.FAILURE || code === JobStatus.CANCELLED;
28
+ let result;
29
+ try {
30
+ result = await this.retrieveResult();
31
+ }
32
+ catch (err) {
33
+ if (!isFailed)
34
+ throw err;
35
+ result = {
36
+ agent_id: "",
37
+ agent_version_id: "",
38
+ inputs: [],
39
+ outputs: [],
40
+ input_tokens: null,
41
+ output_tokens: null,
42
+ };
43
+ }
44
+ result.status = code;
45
+ result.error_message = status.error_message ?? null;
46
+ return result;
28
47
  }
29
48
  if (Date.now() - start > timeoutSeconds * 1000) {
30
49
  throw new Error(`Job ${this.jobId} did not complete within ${timeoutSeconds} seconds`);
@@ -42,7 +61,7 @@ export class Job {
42
61
  export class JobBatch {
43
62
  constructor(opts) {
44
63
  this.completed = {};
45
- this.statusCache = {};
64
+ this.statuses = {};
46
65
  this.agentsApi = opts.agentsApi;
47
66
  this.jobIds = opts.jobIds;
48
67
  this.timeoutSeconds = opts.timeoutSeconds ?? 7200;
@@ -64,45 +83,86 @@ export class JobBatch {
64
83
  if (!pending.length)
65
84
  break;
66
85
  const statusBatch = await this.agentsApi.jobs.retrieveStatusMany(pending);
67
- const failures = statusBatch.filter((s) => s.status === JobStatus.FAILURE || s.status === JobStatus.CANCELLED);
68
- if (failures.length) {
69
- const detail = failures.map((f) => `${f.id}:${f.status ?? "unknown"}`).join(', ');
70
- throw new Error(`Jobs failed or cancelled: ${detail}`);
71
- }
72
86
  const completedIds = [];
73
87
  for (const status of statusBatch) {
74
88
  const code = status.status;
75
- if (code === JobStatus.SUCCESS || code === JobStatus.CACHED) {
89
+ if (code === JobStatus.SUCCESS ||
90
+ code === JobStatus.CACHED ||
91
+ code === JobStatus.FAILURE ||
92
+ code === JobStatus.CANCELLED) {
76
93
  completedIds.push(status.id);
77
94
  }
78
95
  if (code !== undefined && code !== null) {
79
- this.statusCache[status.id] = code;
96
+ this.statuses[status.id] = {
97
+ status: code,
98
+ timestamp: status.timestamp ?? null,
99
+ error_message: status.error_message ?? null,
100
+ };
80
101
  }
81
102
  }
82
103
  if (completedIds.length) {
83
- const resultBatch = await this.agentsApi.jobs.retrieveResultMany(completedIds);
104
+ let resultBatch;
105
+ try {
106
+ resultBatch = await this.agentsApi.jobs.retrieveResultMany(completedIds);
107
+ }
108
+ catch (err) {
109
+ // Only synthesize for failed/cancelled — can't fake results for success/cached
110
+ const failedIds = completedIds.filter((id) => {
111
+ const s = this.statuses[id]?.status;
112
+ return s === JobStatus.FAILURE || s === JobStatus.CANCELLED;
113
+ });
114
+ if (failedIds.length < completedIds.length) {
115
+ // Some successful jobs had their results lost — must propagate
116
+ throw err;
117
+ }
118
+ resultBatch = failedIds.map((id) => ({
119
+ id,
120
+ status: this.statuses[id]?.status ?? null,
121
+ agent_id: null,
122
+ agent_version_id: null,
123
+ inputs: null,
124
+ result: null,
125
+ corrected_outputs: null,
126
+ input_tokens: null,
127
+ output_tokens: null,
128
+ }));
129
+ }
84
130
  for (const res of resultBatch) {
131
+ const jobStatus = res.status ?? this.statuses[res.id]?.status ?? null;
132
+ const isFailed = jobStatus === JobStatus.FAILURE || jobStatus === JobStatus.CANCELLED;
85
133
  if (!res.agent_id || !res.agent_version_id) {
86
- const id = res.id ?? 'unknown';
87
- throw new Error(`Job ${id} missing agent identifiers`);
134
+ if (!isFailed) {
135
+ const id = res.id ?? 'unknown';
136
+ throw new Error(`Job ${id} missing agent identifiers`);
137
+ }
88
138
  }
89
139
  // Use corrected_outputs as fallback if result is null/undefined
90
- const outputs = res.result ?? res.corrected_outputs;
91
- if (outputs == null) {
92
- // Both result and corrected_outputs are null - this may indicate an error or incomplete job
93
- throw new Error(`Job ${res.id} returned null or undefined result`);
140
+ const rawOutputs = res.result ?? res.corrected_outputs;
141
+ let outputs;
142
+ if (rawOutputs == null) {
143
+ if (!isFailed) {
144
+ throw new Error(`Job ${res.id} returned null or undefined result`);
145
+ }
146
+ outputs = [];
94
147
  }
95
- if (!Array.isArray(outputs)) {
96
- // Result exists but is not an array - unexpected format
97
- throw new Error(`Job ${res.id} returned unexpected result format: ${typeof outputs}`);
148
+ else if (!Array.isArray(rawOutputs)) {
149
+ if (!isFailed) {
150
+ throw new Error(`Job ${res.id} returned unexpected result format: ${typeof rawOutputs}`);
151
+ }
152
+ outputs = [];
153
+ }
154
+ else {
155
+ outputs = rawOutputs;
98
156
  }
99
157
  this.completed[res.id] = {
100
- agent_id: res.agent_id,
101
- agent_version_id: res.agent_version_id,
158
+ agent_id: res.agent_id ?? "",
159
+ agent_version_id: res.agent_version_id ?? "",
102
160
  inputs: res.inputs ?? [],
103
161
  input_tokens: res.input_tokens,
104
162
  output_tokens: res.output_tokens,
105
163
  outputs,
164
+ status: jobStatus,
165
+ error_message: this.statuses[res.id]?.error_message ?? null,
106
166
  };
107
167
  }
108
168
  }
@@ -117,14 +177,29 @@ export class JobBatch {
117
177
  return this.jobIds.map((id) => this.completed[id]);
118
178
  }
119
179
  async retrieveStatus() {
120
- const statusMap = { ...this.statusCache };
121
- const toQuery = this.jobIds.filter((id) => statusMap[id] === undefined);
180
+ const statusMap = {};
181
+ const toQuery = [];
182
+ const TERMINAL = new Set([JobStatus.SUCCESS, JobStatus.CACHED, JobStatus.FAILURE, JobStatus.CANCELLED]);
183
+ for (const id of this.jobIds) {
184
+ const cached = this.statuses[id];
185
+ if (cached !== undefined && TERMINAL.has(cached.status)) {
186
+ statusMap[id] = cached;
187
+ }
188
+ else {
189
+ toQuery.push(id);
190
+ }
191
+ }
122
192
  if (toQuery.length) {
123
- const statuses = await this.agentsApi.jobs.retrieveStatusMany(toQuery);
124
- for (const s of statuses) {
193
+ const batchStatuses = await this.agentsApi.jobs.retrieveStatusMany(toQuery);
194
+ for (const s of batchStatuses) {
125
195
  if (s.status !== undefined && s.status !== null) {
126
- statusMap[s.id] = s.status;
127
- this.statusCache[s.id] = s.status;
196
+ const js = {
197
+ status: s.status,
198
+ timestamp: s.timestamp ?? null,
199
+ error_message: s.error_message ?? null,
200
+ };
201
+ this.statuses[s.id] = js;
202
+ statusMap[s.id] = js;
128
203
  }
129
204
  }
130
205
  }
@@ -0,0 +1,19 @@
1
+ export type Policy = {
2
+ id: string;
3
+ name: string;
4
+ description: string;
5
+ organization_id: string;
6
+ current_version_id: string | null;
7
+ created_at: string;
8
+ updated_at: string;
9
+ };
10
+ export type PolicyVersion = {
11
+ id: string;
12
+ version_name: string;
13
+ content: Record<string, unknown>;
14
+ created_at: string;
15
+ updated_at: string;
16
+ policy: Policy | null;
17
+ created_by: Record<string, unknown> | null;
18
+ base_version_id: string | null;
19
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -26,7 +26,7 @@ export type PaginatedResponse<T> = {
26
26
  };
27
27
  export type AgentJobStatus = {
28
28
  status: number;
29
- timestamp: number;
29
+ timestamp: number | null;
30
30
  error_message?: string | null;
31
31
  };
32
32
  export type Reference = {
@@ -43,12 +43,22 @@ export type AgentJobResult = {
43
43
  input_tokens?: number | null;
44
44
  output_tokens?: number | null;
45
45
  outputs: AgentDatum[];
46
+ status?: number | null;
47
+ error_message?: string | null;
46
48
  };
49
+ /** Returns true if the job status is SUCCESS or CACHED. */
50
+ export declare function isJobSuccess(result: AgentJobResult): boolean;
51
+ /** Returns true if the job status is FAILURE or CANCELLED (any non-success terminal state). */
52
+ export declare function isJobFailure(result: AgentJobResult): boolean;
53
+ /** Returns true if the job status is specifically CANCELLED. */
54
+ export declare function isJobCancelled(result: AgentJobResult): boolean;
47
55
  export type AgentJobStatusBatch = {
48
56
  id: string;
49
57
  status?: number | null;
50
58
  created_at?: unknown;
51
59
  last_updated_at?: unknown;
60
+ timestamp?: number | null;
61
+ error_message?: string | null;
52
62
  };
53
63
  export type AgentJobResultBatch = {
54
64
  id: string;
@@ -34,3 +34,15 @@ export function extractReferencesFromOutputs(outputs) {
34
34
  export function getJobReferences(result) {
35
35
  return extractReferencesFromOutputs(result.outputs ?? []);
36
36
  }
37
+ /** Returns true if the job status is SUCCESS or CACHED. */
38
+ export function isJobSuccess(result) {
39
+ return result.status === JobStatus.SUCCESS || result.status === JobStatus.CACHED;
40
+ }
41
+ /** Returns true if the job status is FAILURE or CANCELLED (any non-success terminal state). */
42
+ export function isJobFailure(result) {
43
+ return result.status === JobStatus.FAILURE || result.status === JobStatus.CANCELLED;
44
+ }
45
+ /** Returns true if the job status is specifically CANCELLED. */
46
+ export function isJobCancelled(result) {
47
+ return result.status === JobStatus.CANCELLED;
48
+ }
@@ -26,5 +26,5 @@ export declare class RoeHTTPClient {
26
26
  private executeWithFormData;
27
27
  put<T>(url: string, json?: Record<string, unknown>, params?: Record<string, unknown>): Promise<T>;
28
28
  delete(url: string, params?: Record<string, unknown>): Promise<void>;
29
- postWithDynamicInputs<T>(url: string, inputs: Record<string, unknown>, params?: Record<string, unknown>): Promise<T>;
29
+ postWithDynamicInputs<T>(url: string, inputs: Record<string, unknown>, params?: Record<string, unknown>, metadata?: Record<string, unknown>): Promise<T>;
30
30
  }
@@ -196,7 +196,7 @@ export class RoeHTTPClient {
196
196
  delete(url, params) {
197
197
  return this.execute(() => this.client.delete(url, { params }));
198
198
  }
199
- async postWithDynamicInputs(url, inputs, params) {
199
+ async postWithDynamicInputs(url, inputs, params, metadata) {
200
200
  const formData = {};
201
201
  const files = {};
202
202
  // Process inputs and detect file paths asynchronously
@@ -224,6 +224,9 @@ export class RoeHTTPClient {
224
224
  formData[key] = value;
225
225
  }
226
226
  }
227
+ if (metadata !== undefined) {
228
+ formData.metadata = JSON.stringify(metadata);
229
+ }
227
230
  return this.post({ url, formData, files, params });
228
231
  }
229
232
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "roe-typescript",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
4
4
  "description": "TypeScript SDK for the Roe AI API (feature parity with roe-python).",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",