@introspection-sdk/introspection-node 0.4.10 → 0.5.1
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/.turbo/turbo-build.log +1 -1
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +4 -1
- package/dist/index.js.map +1 -1
- package/dist/pagination.d.ts +38 -0
- package/dist/pagination.d.ts.map +1 -0
- package/dist/pagination.js +43 -0
- package/dist/pagination.js.map +1 -0
- package/dist/resources/experiments.d.ts +9 -3
- package/dist/resources/experiments.d.ts.map +1 -1
- package/dist/resources/experiments.js +10 -13
- package/dist/resources/experiments.js.map +1 -1
- package/dist/resources/recipes.d.ts +9 -4
- package/dist/resources/recipes.d.ts.map +1 -1
- package/dist/resources/recipes.js +10 -13
- package/dist/resources/recipes.js.map +1 -1
- package/dist/resources/runtimes.d.ts +9 -3
- package/dist/resources/runtimes.d.ts.map +1 -1
- package/dist/resources/runtimes.js +22 -22
- package/dist/resources/runtimes.js.map +1 -1
- package/dist/runner-resources/conversations.d.ts +73 -0
- package/dist/runner-resources/conversations.d.ts.map +1 -0
- package/dist/runner-resources/conversations.js +161 -0
- package/dist/runner-resources/conversations.js.map +1 -0
- package/dist/runner-resources/files.d.ts +18 -8
- package/dist/runner-resources/files.d.ts.map +1 -1
- package/dist/runner-resources/files.js +29 -31
- package/dist/runner-resources/files.js.map +1 -1
- package/dist/runner-resources/tasks.d.ts +9 -3
- package/dist/runner-resources/tasks.d.ts.map +1 -1
- package/dist/runner-resources/tasks.js +10 -12
- package/dist/runner-resources/tasks.js.map +1 -1
- package/dist/runner.d.ts +5 -2
- package/dist/runner.d.ts.map +1 -1
- package/dist/runner.js +6 -2
- package/dist/runner.js.map +1 -1
- package/package.json +6 -6
- package/src/index.ts +23 -1
- package/src/pagination.ts +69 -0
- package/src/resources/experiments.ts +17 -16
- package/src/resources/recipes.ts +17 -16
- package/src/resources/runtimes.ts +28 -25
- package/src/runner-resources/conversations.ts +202 -0
- package/src/runner-resources/files.ts +56 -40
- package/src/runner-resources/tasks.ts +17 -15
- package/src/runner.ts +6 -2
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
import { Paginated } from "@introspection-sdk/types";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Adapts a wire pagination protocol for {@link Paginator}: how to fetch
|
|
5
|
+
* a page at a given cursor, pull the items out of it, and find the
|
|
6
|
+
* cursor for the next page (`undefined` once the list is exhausted).
|
|
7
|
+
*/
|
|
8
|
+
export interface PageSource<T, TPage> {
|
|
9
|
+
/** Fetch the page at `cursor` (`undefined` = the first page). */
|
|
10
|
+
fetch(cursor: string | undefined): Promise<TPage>;
|
|
11
|
+
/** Items contained in `page`, in iteration order. */
|
|
12
|
+
items(page: TPage): T[];
|
|
13
|
+
/** Cursor for the page after `page`, or `undefined` when exhausted. */
|
|
14
|
+
next(page: TPage): string | undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* A lazy, auto-paging collection.
|
|
19
|
+
*
|
|
20
|
+
* - `await listing` resolves to the FIRST page, preserving the wire
|
|
21
|
+
* envelope's metadata (counts, `has_more`, …).
|
|
22
|
+
* - `for await (const item of listing)` streams every item across all
|
|
23
|
+
* pages, fetching each page only as the iterator reaches it; stop
|
|
24
|
+
* early to stop fetching.
|
|
25
|
+
*/
|
|
26
|
+
export class Paginator<T, TPage = Paginated<T>>
|
|
27
|
+
implements AsyncIterable<T>, PromiseLike<TPage>
|
|
28
|
+
{
|
|
29
|
+
constructor(
|
|
30
|
+
private readonly source: PageSource<T, TPage>,
|
|
31
|
+
private readonly start?: string,
|
|
32
|
+
) {}
|
|
33
|
+
|
|
34
|
+
/** Thenable: resolves to the first page (no further pages fetched). */
|
|
35
|
+
then<R1 = TPage, R2 = never>(
|
|
36
|
+
onfulfilled?: ((page: TPage) => R1 | PromiseLike<R1>) | null,
|
|
37
|
+
onrejected?: ((reason: unknown) => R2 | PromiseLike<R2>) | null,
|
|
38
|
+
): PromiseLike<R1 | R2> {
|
|
39
|
+
return this.source.fetch(this.start).then(onfulfilled, onrejected);
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
async *[Symbol.asyncIterator](): AsyncIterator<T> {
|
|
43
|
+
let cursor = this.start;
|
|
44
|
+
do {
|
|
45
|
+
const page = await this.source.fetch(cursor);
|
|
46
|
+
for (const item of this.source.items(page)) yield item;
|
|
47
|
+
cursor = this.source.next(page);
|
|
48
|
+
} while (cursor !== undefined);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Build a {@link Paginator} over the standard Introspection cursor
|
|
54
|
+
* envelope ({@link Paginated}): items live in `records` and the next
|
|
55
|
+
* page is reached via the opaque `next` token.
|
|
56
|
+
*/
|
|
57
|
+
export function cursorPaginate<T>(
|
|
58
|
+
fetch: (next: string | undefined) => Promise<Paginated<T>>,
|
|
59
|
+
start?: string,
|
|
60
|
+
): Paginator<T, Paginated<T>> {
|
|
61
|
+
return new Paginator(
|
|
62
|
+
{
|
|
63
|
+
fetch,
|
|
64
|
+
items: (page) => page.records,
|
|
65
|
+
next: (page) => page.next ?? undefined,
|
|
66
|
+
},
|
|
67
|
+
start,
|
|
68
|
+
);
|
|
69
|
+
}
|
|
@@ -11,6 +11,7 @@ import type {
|
|
|
11
11
|
} from "@introspection-sdk/types";
|
|
12
12
|
import type { HttpClient } from "../http.js";
|
|
13
13
|
import type { IntrospectionClient } from "../client.js";
|
|
14
|
+
import { Paginator, cursorPaginate } from "../pagination.js";
|
|
14
15
|
import { Runner, type RunnerSource } from "../runner.js";
|
|
15
16
|
|
|
16
17
|
interface ExperimentRunRequestBody {
|
|
@@ -36,21 +37,22 @@ export class ExperimentsApi {
|
|
|
36
37
|
private readonly client: IntrospectionClient,
|
|
37
38
|
) {}
|
|
38
39
|
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
40
|
+
/**
|
|
41
|
+
* List experiments matching `params`. `await` the result for the first
|
|
42
|
+
* page, or `for await` it to stream every experiment across pages
|
|
43
|
+
* (fetched lazily — `limit` sets the page size, `next` the starting
|
|
44
|
+
* cursor; stop early to stop fetching).
|
|
45
|
+
*/
|
|
46
|
+
list(params: ExperimentListParams): Paginator<Experiment> {
|
|
47
|
+
return cursorPaginate(
|
|
48
|
+
(next) =>
|
|
49
|
+
this.http.request<Paginated<Experiment>>({
|
|
50
|
+
method: "GET",
|
|
51
|
+
path: "/v1/experiments",
|
|
52
|
+
query: { ...params, next } as unknown as Record<string, unknown>,
|
|
53
|
+
}),
|
|
54
|
+
params.next,
|
|
55
|
+
);
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
get(id: Uuid): Promise<Experiment> {
|
|
@@ -157,7 +159,6 @@ export function attachExperiments(
|
|
|
157
159
|
new ExperimentHandle(api, id);
|
|
158
160
|
const hybrid = factory as ExperimentsApi & ExperimentHandleFactory;
|
|
159
161
|
hybrid.list = api.list.bind(api);
|
|
160
|
-
hybrid.listAll = api.listAll.bind(api);
|
|
161
162
|
hybrid.get = api.get.bind(api);
|
|
162
163
|
hybrid.create = api.create.bind(api);
|
|
163
164
|
hybrid.update = api.update.bind(api);
|
package/src/resources/recipes.ts
CHANGED
|
@@ -7,6 +7,7 @@ import type {
|
|
|
7
7
|
Uuid,
|
|
8
8
|
} from "@introspection-sdk/types";
|
|
9
9
|
import type { HttpClient } from "../http.js";
|
|
10
|
+
import { Paginator, cursorPaginate } from "../pagination.js";
|
|
10
11
|
|
|
11
12
|
/**
|
|
12
13
|
* Programmatic CRUD for `/v1/recipes` on the CP.
|
|
@@ -18,22 +19,22 @@ import type { HttpClient } from "../http.js";
|
|
|
18
19
|
export class RecipesApi {
|
|
19
20
|
constructor(private readonly http: HttpClient) {}
|
|
20
21
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
22
|
+
/**
|
|
23
|
+
* List recipes matching `params`. `await` the result for the first
|
|
24
|
+
* page, or `for await` it to stream every recipe across pages (fetched
|
|
25
|
+
* lazily — `limit` sets the page size, `next` the starting cursor; stop
|
|
26
|
+
* early to stop fetching).
|
|
27
|
+
*/
|
|
28
|
+
list(params: RecipeListParams): Paginator<Recipe> {
|
|
29
|
+
return cursorPaginate(
|
|
30
|
+
(next) =>
|
|
31
|
+
this.http.request<Paginated<Recipe>>({
|
|
32
|
+
method: "GET",
|
|
33
|
+
path: "/v1/recipes",
|
|
34
|
+
query: { ...params, next } as unknown as Record<string, unknown>,
|
|
35
|
+
}),
|
|
36
|
+
params.next,
|
|
37
|
+
);
|
|
37
38
|
}
|
|
38
39
|
|
|
39
40
|
get(id: Uuid): Promise<Recipe> {
|
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
import { NotFoundError } from "@introspection-sdk/types";
|
|
13
13
|
import type { HttpClient } from "../http.js";
|
|
14
14
|
import type { IntrospectionClient } from "../client.js";
|
|
15
|
+
import { Paginator, cursorPaginate } from "../pagination.js";
|
|
15
16
|
import { Runner, type RunnerSource } from "../runner.js";
|
|
16
17
|
|
|
17
18
|
const UUID_RE =
|
|
@@ -50,21 +51,22 @@ export class RuntimesApi {
|
|
|
50
51
|
private readonly client: IntrospectionClient,
|
|
51
52
|
) {}
|
|
52
53
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
54
|
+
/**
|
|
55
|
+
* List runtimes matching `params`. `await` the result for the first
|
|
56
|
+
* page, or `for await` it to stream every runtime across pages (fetched
|
|
57
|
+
* lazily — `limit` sets the page size, `next` the starting cursor; stop
|
|
58
|
+
* early to stop fetching).
|
|
59
|
+
*/
|
|
60
|
+
list(params: RuntimeListParams): Paginator<Runtime> {
|
|
61
|
+
return cursorPaginate(
|
|
62
|
+
(next) =>
|
|
63
|
+
this.http.request<Paginated<Runtime>>({
|
|
64
|
+
method: "GET",
|
|
65
|
+
path: "/v1/runtimes",
|
|
66
|
+
query: { ...params, next } as unknown as Record<string, unknown>,
|
|
67
|
+
}),
|
|
68
|
+
params.next,
|
|
69
|
+
);
|
|
68
70
|
}
|
|
69
71
|
|
|
70
72
|
get(id: Uuid, params: { project_id?: Uuid }): Promise<Runtime> {
|
|
@@ -101,16 +103,18 @@ export class RuntimesApi {
|
|
|
101
103
|
|
|
102
104
|
/** Resolve a name to a runtime by querying `/v1/runtimes?name=…`. */
|
|
103
105
|
async resolveByName(name: string, projectId?: Uuid): Promise<Runtime> {
|
|
104
|
-
const
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
code: "not_found",
|
|
111
|
-
});
|
|
106
|
+
for await (const r of this.list({
|
|
107
|
+
project_id: projectId,
|
|
108
|
+
name,
|
|
109
|
+
limit: 2,
|
|
110
|
+
})) {
|
|
111
|
+
if (r.name === name) return r;
|
|
112
112
|
}
|
|
113
|
-
|
|
113
|
+
throw new NotFoundError({
|
|
114
|
+
message: `Runtime '${name}' not found${projectId ? ` in project ${projectId}` : ""}`,
|
|
115
|
+
status: 404,
|
|
116
|
+
code: "not_found",
|
|
117
|
+
});
|
|
114
118
|
}
|
|
115
119
|
|
|
116
120
|
/** POST `/v1/runtimes/{id}/run` and wrap the result in a `Runner`. */
|
|
@@ -219,7 +223,6 @@ export function attachRuntimes(
|
|
|
219
223
|
// the methods can be invoked off the proxy without losing `this`.
|
|
220
224
|
const hybrid = factory as RuntimesApi & RuntimeHandleFactory;
|
|
221
225
|
hybrid.list = api.list.bind(api);
|
|
222
|
-
hybrid.listAll = api.listAll.bind(api);
|
|
223
226
|
hybrid.get = api.get.bind(api);
|
|
224
227
|
hybrid.create = api.create.bind(api);
|
|
225
228
|
hybrid.update = api.update.bind(api);
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
ConversationItem,
|
|
3
|
+
ConversationItemInclude,
|
|
4
|
+
ConversationItemList,
|
|
5
|
+
ConversationItemListParams,
|
|
6
|
+
ConversationListParams,
|
|
7
|
+
ConversationResponse,
|
|
8
|
+
ConversationSummary,
|
|
9
|
+
MessagePart,
|
|
10
|
+
Paginated,
|
|
11
|
+
ToolCallResponsePart,
|
|
12
|
+
} from "@introspection-sdk/types";
|
|
13
|
+
import { HttpClient } from "../http.js";
|
|
14
|
+
import { Paginator, cursorPaginate } from "../pagination.js";
|
|
15
|
+
|
|
16
|
+
/** Includes requested when building a {@link ConversationResponse}. */
|
|
17
|
+
const RESPONSE_INCLUDES: ConversationItemInclude[] = [
|
|
18
|
+
"gen_ai.input.messages",
|
|
19
|
+
"gen_ai.system_instructions",
|
|
20
|
+
"gen_ai.tool.definitions",
|
|
21
|
+
];
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Items of a conversation (`/v1/conversations/{id}/items`). Read-only.
|
|
25
|
+
*
|
|
26
|
+
* Paging is OpenAI-style underneath: the envelope has NO `next` token —
|
|
27
|
+
* `list()` drives `after` = the previous page's `last_id` while
|
|
28
|
+
* `has_more` is true.
|
|
29
|
+
*/
|
|
30
|
+
export class ConversationItemsApi {
|
|
31
|
+
constructor(private readonly http: HttpClient) {}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* List items of a conversation. `await` the result for the first page
|
|
35
|
+
* (an OpenAI-style {@link ConversationItemList} envelope), or
|
|
36
|
+
* `for await` it to stream every item across pages (fetched lazily —
|
|
37
|
+
* `limit` sets the page size, `after` the starting cursor; stop early to
|
|
38
|
+
* stop fetching). Pass `order: "asc"` to walk the transcript from the
|
|
39
|
+
* start.
|
|
40
|
+
*
|
|
41
|
+
* Items carry the turn-local delta in `input_messages` — only the
|
|
42
|
+
* messages new to that turn. Use `get()` for the full input history.
|
|
43
|
+
*/
|
|
44
|
+
list(
|
|
45
|
+
conversationId: string,
|
|
46
|
+
params?: ConversationItemListParams,
|
|
47
|
+
): Paginator<ConversationItem, ConversationItemList> {
|
|
48
|
+
return new Paginator(
|
|
49
|
+
{
|
|
50
|
+
fetch: (after) =>
|
|
51
|
+
this.http.request<ConversationItemList>({
|
|
52
|
+
method: "GET",
|
|
53
|
+
path: `/v1/conversations/${encodeURIComponent(conversationId)}/items`,
|
|
54
|
+
query: { ...params, after } as Record<string, unknown>,
|
|
55
|
+
}),
|
|
56
|
+
items: (page) => page.data,
|
|
57
|
+
next: (page) =>
|
|
58
|
+
page.has_more && page.last_id !== null ? page.last_id : undefined,
|
|
59
|
+
},
|
|
60
|
+
params?.after,
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Fetch a single conversation item. Unlike the list route, the
|
|
66
|
+
* detail's `input_messages` is the FULL input history for that span.
|
|
67
|
+
*/
|
|
68
|
+
get(
|
|
69
|
+
conversationId: string,
|
|
70
|
+
itemId: string,
|
|
71
|
+
params?: { include?: ConversationItemInclude[] },
|
|
72
|
+
): Promise<ConversationItem> {
|
|
73
|
+
return this.http.request<ConversationItem>({
|
|
74
|
+
method: "GET",
|
|
75
|
+
path: `/v1/conversations/${encodeURIComponent(conversationId)}/items/${encodeURIComponent(itemId)}`,
|
|
76
|
+
query: params as Record<string, unknown> | undefined,
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Read-only Conversations API (`/v1/conversations`).
|
|
83
|
+
*
|
|
84
|
+
* Both `list()` and `items.list()` are auto-paging async generators,
|
|
85
|
+
* but they drive different wire protocols underneath: `list()` walks
|
|
86
|
+
* the standard Introspection cursor envelope's opaque `next` token,
|
|
87
|
+
* while `items.list()` walks an OpenAI-style envelope via `after` =
|
|
88
|
+
* the previous page's `last_id` while `has_more` is true.
|
|
89
|
+
*/
|
|
90
|
+
export class ConversationsApi {
|
|
91
|
+
/** Items of a conversation — `conversations.items.list(...)` etc. */
|
|
92
|
+
readonly items: ConversationItemsApi;
|
|
93
|
+
|
|
94
|
+
constructor(private readonly http: HttpClient) {
|
|
95
|
+
this.items = new ConversationItemsApi(http);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* List conversation summaries matching `params`. `await` the result for
|
|
100
|
+
* the first page, or `for await` it to stream every summary across
|
|
101
|
+
* pages (fetched lazily — `limit` sets the page size, `next` the
|
|
102
|
+
* starting cursor; stop early to stop fetching).
|
|
103
|
+
*/
|
|
104
|
+
list(params?: ConversationListParams): Paginator<ConversationSummary> {
|
|
105
|
+
return cursorPaginate(
|
|
106
|
+
(next) =>
|
|
107
|
+
this.http.request<Paginated<ConversationSummary>>({
|
|
108
|
+
method: "GET",
|
|
109
|
+
path: "/v1/conversations",
|
|
110
|
+
query: { ...params, next } as Record<string, unknown>,
|
|
111
|
+
}),
|
|
112
|
+
params?.next,
|
|
113
|
+
);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Responses-API-style retrieve: load the state of a conversation as of
|
|
118
|
+
* one item — the full input history, output, system instructions, and
|
|
119
|
+
* tool definitions of that turn.
|
|
120
|
+
*
|
|
121
|
+
* When `itemId` is omitted, the latest LLM turn is used: the first
|
|
122
|
+
* item (in descending order) whose `node_type` is `"assistant"` or
|
|
123
|
+
* whose `operation_name` is `"chat"`, falling back to the first item
|
|
124
|
+
* with a non-null `output_message`. Returns `null` when the
|
|
125
|
+
* conversation has no items.
|
|
126
|
+
*
|
|
127
|
+
* For the full per-turn transcript instead, iterate
|
|
128
|
+
* `items.list(conversationId, { order: "asc" })`.
|
|
129
|
+
*/
|
|
130
|
+
async retrieve(
|
|
131
|
+
conversationId: string,
|
|
132
|
+
itemId?: string,
|
|
133
|
+
): Promise<ConversationResponse | null> {
|
|
134
|
+
const targetId = itemId ?? (await this.findLatestTurnId(conversationId));
|
|
135
|
+
if (targetId === null) return null;
|
|
136
|
+
|
|
137
|
+
const detail = await this.items.get(conversationId, targetId, {
|
|
138
|
+
include: RESPONSE_INCLUDES,
|
|
139
|
+
});
|
|
140
|
+
const outputMessages =
|
|
141
|
+
detail.gen_ai_output_messages ??
|
|
142
|
+
(detail.output_message ? [detail.output_message] : []);
|
|
143
|
+
return {
|
|
144
|
+
conversation_id: conversationId,
|
|
145
|
+
response_id: detail.response_id ?? null,
|
|
146
|
+
item_id: detail.id,
|
|
147
|
+
created_at: detail.created_at,
|
|
148
|
+
model:
|
|
149
|
+
detail.model_name ??
|
|
150
|
+
detail.response_model ??
|
|
151
|
+
detail.request_model ??
|
|
152
|
+
null,
|
|
153
|
+
provider_name: detail.provider_name ?? null,
|
|
154
|
+
// The single-item route returns the FULL input history here.
|
|
155
|
+
input_messages: normalizeMessages(detail.input_messages),
|
|
156
|
+
output_messages: normalizeMessages(outputMessages),
|
|
157
|
+
system_instructions: detail.system_instructions ?? null,
|
|
158
|
+
tool_definitions: detail.tool_definitions ?? null,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/** Scan items in descending order for the most recent LLM turn. */
|
|
163
|
+
private async findLatestTurnId(
|
|
164
|
+
conversationId: string,
|
|
165
|
+
): Promise<string | null> {
|
|
166
|
+
let fallback: ConversationItem | null = null;
|
|
167
|
+
for await (const item of this.items.list(conversationId, {
|
|
168
|
+
order: "desc",
|
|
169
|
+
})) {
|
|
170
|
+
if (item.node_type === "assistant" || item.operation_name === "chat") {
|
|
171
|
+
return item.id;
|
|
172
|
+
}
|
|
173
|
+
if (fallback === null && item.output_message != null) fallback = item;
|
|
174
|
+
}
|
|
175
|
+
return fallback?.id ?? null;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Defensive normalization for {@link ConversationsApi.retrieve} only:
|
|
181
|
+
* older DP deployments emitted `tool_call_response` parts with a legacy
|
|
182
|
+
* `result` key instead of the semconv `response` key. Map it across so
|
|
183
|
+
* replayed history is always semconv-shaped.
|
|
184
|
+
*/
|
|
185
|
+
function normalizeMessages<T extends { parts: MessagePart[] }>(
|
|
186
|
+
messages: T[],
|
|
187
|
+
): T[] {
|
|
188
|
+
return messages.map((message) => ({
|
|
189
|
+
...message,
|
|
190
|
+
parts: message.parts.map(normalizePart),
|
|
191
|
+
}));
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function normalizePart(part: MessagePart): MessagePart {
|
|
195
|
+
if (part.type !== "tool_call_response") return part;
|
|
196
|
+
const legacy = part as ToolCallResponsePart & { result?: unknown };
|
|
197
|
+
if (legacy.response !== undefined || legacy.result === undefined) {
|
|
198
|
+
return part;
|
|
199
|
+
}
|
|
200
|
+
const { result, ...rest } = legacy;
|
|
201
|
+
return { ...rest, response: result };
|
|
202
|
+
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import type {
|
|
2
2
|
File as FileResource,
|
|
3
|
+
FileCreateOptions,
|
|
3
4
|
FileCreateTextParams,
|
|
4
5
|
FileListParams,
|
|
5
6
|
FileType,
|
|
@@ -8,6 +9,7 @@ import type {
|
|
|
8
9
|
Paginated,
|
|
9
10
|
} from "@introspection-sdk/types";
|
|
10
11
|
import { HttpClient } from "../http.js";
|
|
12
|
+
import { Paginator, cursorPaginate } from "../pagination.js";
|
|
11
13
|
|
|
12
14
|
export type FileUploadBody =
|
|
13
15
|
| { file: Blob; name?: string; file_type?: FileType }
|
|
@@ -21,24 +23,21 @@ export type FileUploadBody =
|
|
|
21
23
|
export class FileVersionsApi {
|
|
22
24
|
constructor(private readonly http: HttpClient) {}
|
|
23
25
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
for (const f of page.records) yield f;
|
|
40
|
-
next = page.next ?? undefined;
|
|
41
|
-
} while (next);
|
|
26
|
+
/**
|
|
27
|
+
* List versions of a file. `await` the result for the first page, or
|
|
28
|
+
* `for await` it to stream every version across pages (fetched lazily;
|
|
29
|
+
* stop early to stop fetching).
|
|
30
|
+
*/
|
|
31
|
+
list(fileId: string, params?: ListParams): Paginator<FileResource> {
|
|
32
|
+
return cursorPaginate(
|
|
33
|
+
(next) =>
|
|
34
|
+
this.http.request<Paginated<FileResource>>({
|
|
35
|
+
method: "GET",
|
|
36
|
+
path: `/v1/files/${encodeURIComponent(fileId)}/versions`,
|
|
37
|
+
query: { ...params, next } as Record<string, unknown>,
|
|
38
|
+
}),
|
|
39
|
+
params?.next,
|
|
40
|
+
);
|
|
42
41
|
}
|
|
43
42
|
|
|
44
43
|
get(fileId: string, versionId: string): Promise<FileResource> {
|
|
@@ -48,8 +47,12 @@ export class FileVersionsApi {
|
|
|
48
47
|
});
|
|
49
48
|
}
|
|
50
49
|
|
|
51
|
-
create(
|
|
52
|
-
|
|
50
|
+
create(
|
|
51
|
+
fileId: string,
|
|
52
|
+
body: FileUploadBody,
|
|
53
|
+
options?: FileCreateOptions,
|
|
54
|
+
): Promise<FileResource> {
|
|
55
|
+
const form = toFormData(body, options);
|
|
53
56
|
return this.http.request<FileResource>({
|
|
54
57
|
method: "POST",
|
|
55
58
|
path: `/v1/files/${encodeURIComponent(fileId)}/versions`,
|
|
@@ -65,25 +68,29 @@ export class FilesApi {
|
|
|
65
68
|
this.versions = new FileVersionsApi(http);
|
|
66
69
|
}
|
|
67
70
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
/**
|
|
72
|
+
* List files matching `params`. `await` the result for the first page,
|
|
73
|
+
* or `for await` it to stream every file across pages (fetched lazily —
|
|
74
|
+
* `limit` sets the page size, `next` the starting cursor; stop early to
|
|
75
|
+
* stop fetching).
|
|
76
|
+
*/
|
|
77
|
+
list(params?: FileListParams): Paginator<FileResource> {
|
|
78
|
+
return cursorPaginate(
|
|
79
|
+
(next) =>
|
|
80
|
+
this.http.request<Paginated<FileResource>>({
|
|
81
|
+
method: "GET",
|
|
82
|
+
path: "/v1/files",
|
|
83
|
+
query: { ...params, next } as Record<string, unknown>,
|
|
84
|
+
}),
|
|
85
|
+
params?.next,
|
|
86
|
+
);
|
|
74
87
|
}
|
|
75
88
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
next = page.next ?? undefined;
|
|
82
|
-
} while (next);
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
upload(body: FileUploadBody): Promise<FileResource> {
|
|
86
|
-
const form = toFormData(body);
|
|
89
|
+
upload(
|
|
90
|
+
body: FileUploadBody,
|
|
91
|
+
options?: FileCreateOptions,
|
|
92
|
+
): Promise<FileResource> {
|
|
93
|
+
const form = toFormData(body, options);
|
|
87
94
|
return this.http.request<FileResource>({
|
|
88
95
|
method: "POST",
|
|
89
96
|
path: "/v1/files",
|
|
@@ -91,11 +98,16 @@ export class FilesApi {
|
|
|
91
98
|
});
|
|
92
99
|
}
|
|
93
100
|
|
|
94
|
-
createText(
|
|
101
|
+
createText(
|
|
102
|
+
body: FileCreateTextParams,
|
|
103
|
+
options?: FileCreateOptions,
|
|
104
|
+
): Promise<FileResource> {
|
|
95
105
|
return this.http.request<FileResource>({
|
|
96
106
|
method: "POST",
|
|
97
107
|
path: "/v1/files",
|
|
98
|
-
body
|
|
108
|
+
body: options?.visibility
|
|
109
|
+
? { ...body, visibility: options.visibility }
|
|
110
|
+
: body,
|
|
99
111
|
});
|
|
100
112
|
}
|
|
101
113
|
|
|
@@ -139,7 +151,10 @@ export class FilesApi {
|
|
|
139
151
|
}
|
|
140
152
|
}
|
|
141
153
|
|
|
142
|
-
function toFormData(
|
|
154
|
+
function toFormData(
|
|
155
|
+
body: FileUploadBody,
|
|
156
|
+
options?: FileCreateOptions,
|
|
157
|
+
): FormData {
|
|
143
158
|
const fd = new FormData();
|
|
144
159
|
if ("file" in body && body.file instanceof Blob) {
|
|
145
160
|
fd.append("file", body.file, body.name);
|
|
@@ -159,5 +174,6 @@ function toFormData(body: FileUploadBody): FormData {
|
|
|
159
174
|
}
|
|
160
175
|
if (body.name) fd.append("name", body.name);
|
|
161
176
|
if (body.file_type) fd.append("file_type", body.file_type);
|
|
177
|
+
if (options?.visibility) fd.append("visibility", options.visibility);
|
|
162
178
|
return fd;
|
|
163
179
|
}
|
|
@@ -12,6 +12,7 @@ import type {
|
|
|
12
12
|
TaskUpdateParams,
|
|
13
13
|
} from "@introspection-sdk/types";
|
|
14
14
|
import { HttpClient } from "../http.js";
|
|
15
|
+
import { Paginator, cursorPaginate } from "../pagination.js";
|
|
15
16
|
import { parseSse } from "../streaming.js";
|
|
16
17
|
|
|
17
18
|
export interface StartParams extends TaskCreateParams {
|
|
@@ -84,21 +85,22 @@ export class TasksApi {
|
|
|
84
85
|
this.runs = new TaskRunsApi(http);
|
|
85
86
|
}
|
|
86
87
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
88
|
+
/**
|
|
89
|
+
* List tasks matching `params`. `await` the result for the first page,
|
|
90
|
+
* or `for await` it to stream every task across pages (fetched lazily —
|
|
91
|
+
* `limit` sets the page size, `next` the starting cursor; stop early to
|
|
92
|
+
* stop fetching).
|
|
93
|
+
*/
|
|
94
|
+
list(params?: TaskListParams): Paginator<Task> {
|
|
95
|
+
return cursorPaginate(
|
|
96
|
+
(next) =>
|
|
97
|
+
this.http.request<Paginated<Task>>({
|
|
98
|
+
method: "GET",
|
|
99
|
+
path: "/v1/tasks",
|
|
100
|
+
query: { ...params, next } as Record<string, unknown>,
|
|
101
|
+
}),
|
|
102
|
+
params?.next,
|
|
103
|
+
);
|
|
102
104
|
}
|
|
103
105
|
|
|
104
106
|
create(body: TaskCreateParams): Promise<TaskCreateResponse> {
|