Skip to content

Commit b97f535

Browse files
committed
feat: add file management actions and loaders
- Introduced new actions for file management: delete and upload. - Added loaders for retrieving and listing files. - Updated the search action to include an optional filter parameter. - Modified the manifest to include new actions and loaders. - Removed the Content-Type header from API requests for better flexibility.
1 parent 6c85200 commit b97f535

9 files changed

Lines changed: 292 additions & 9 deletions

File tree

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import { AppContext } from "../mod.ts";
2+
3+
export interface Props {
4+
fileId: string;
5+
}
6+
7+
/**
8+
* @title Delete File
9+
* @name Delete File
10+
* @description Deletes a file from the assistant
11+
*/
12+
const action = async (
13+
props: Props,
14+
_req: Request,
15+
ctx: AppContext,
16+
) => {
17+
const response = await ctx.client
18+
["DELETE /assistant/files/:assistant_name/:assistant_file_id"]({
19+
assistant_name: ctx.assistant,
20+
assistant_file_id: props.fileId,
21+
});
22+
23+
if (response.status !== 200) {
24+
return {
25+
success: false,
26+
error: "Failed to delete file " + response.statusText,
27+
};
28+
}
29+
30+
return {
31+
success: true,
32+
};
33+
};
34+
35+
export default action;

pinecone-assistant/actions/search.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,11 @@ export interface Props {
66
*/
77
query: string;
88

9+
/**
10+
* @description Optionally filter which documents can be retrieved using the following metadata fields. Example: { "type": "faq_entry" }
11+
*/
12+
filter?: string;
13+
914
/**
1015
* @description The number of context snippets to retrieve. Defaults to 15.
1116
*/
@@ -21,7 +26,8 @@ interface QueryResult {
2126

2227
/**
2328
* @title Get Context
24-
* @description Retrieves relevant document snippets from the assistant's knowledge base.Returns an array of text snippets from the most relevant documents. The snippets are formatted as JSON objects with the fields: - file_name: The name of the file containing the snippet - pages: The pages of the file containing the snippet - content: The snippet content You can use the 'top_k' parameter to control result count (default: 15). Recommended top_k: a few (5-8) for simple/narrow queries, 10-20 for complex/broad topics.
29+
* @name Get Context
30+
* @description Retrieves relevant document snippets from the assistant's knowledge base. Returns an array of text snippets from the most relevant documents. The snippets are formatted as JSON objects with the fields: - file_name: The name of the file containing the snippet - pages: The pages of the file containing the snippet - content: The snippet content You can use the 'top_k' parameter to control result count (default: 15). Recommended top_k: a few (5-8) for simple/narrow queries, 10-20 for complex/broad topics.
2531
*/
2632
const action = async (
2733
props: Props,
@@ -35,6 +41,7 @@ const action = async (
3541
}, {
3642
body: {
3743
query: props.query,
44+
filter: props.filter ? JSON.parse(props.filter) : undefined,
3845
},
3946
});
4047

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
import { AppContext } from "../mod.ts";
2+
3+
export interface Props {
4+
/**
5+
* @description The URL of the file to upload
6+
*/
7+
fileUrl?: string;
8+
9+
/**
10+
* @description The content of the file to upload (text only)
11+
*/
12+
fileContent?: string;
13+
14+
/**
15+
* @description The optional name of the file with extension (if not provided, the file will be named "file-${timestamp}.txt")
16+
*/
17+
fileName?: string;
18+
19+
/**
20+
* @description The optional metadata to attach to the file
21+
*/
22+
metadata?: string;
23+
}
24+
25+
/**
26+
* @title Upload File
27+
* @name Upload File
28+
* @description Uploads a file to the assistant
29+
*/
30+
const action = async (
31+
props: Props,
32+
_req: Request,
33+
ctx: AppContext,
34+
) => {
35+
if (!props.fileUrl && !props.fileContent) {
36+
return {
37+
success: false,
38+
error: "No file URL or content provided",
39+
};
40+
}
41+
42+
let fileBuffer: ArrayBuffer | null = null;
43+
let contentType: string | null = null;
44+
45+
if (props.fileUrl) {
46+
const fileResponse = await fetch(props.fileUrl);
47+
contentType = fileResponse.headers.get("content-type");
48+
fileBuffer = await fileResponse.arrayBuffer();
49+
}
50+
51+
if (props.fileContent) {
52+
fileBuffer = new TextEncoder().encode(props.fileContent)
53+
.buffer as ArrayBuffer;
54+
contentType = "text/plain";
55+
}
56+
57+
if (!fileBuffer) {
58+
return {
59+
success: false,
60+
error:
61+
"Could not create a file buffer from the provided file URL or content",
62+
};
63+
}
64+
65+
const file = new File(
66+
[fileBuffer],
67+
props.fileName || `file-${Date.now()}.txt`,
68+
{ type: contentType || "application/octet-stream" },
69+
);
70+
71+
const formData = new FormData();
72+
formData.append("file", file);
73+
74+
const response = await ctx.client
75+
["POST /assistant/files/:assistant_name"]({
76+
assistant_name: ctx.assistant,
77+
metadata: props.metadata,
78+
}, {
79+
body: formData,
80+
});
81+
82+
const result = await response.json();
83+
console.log({ result });
84+
85+
if (result.error_message) {
86+
return {
87+
success: false,
88+
error: result.error_message,
89+
};
90+
}
91+
92+
return {
93+
success: true,
94+
file: result,
95+
};
96+
};
97+
98+
export default action;

pinecone-assistant/loaders/get.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { AppContext } from "../mod.ts";
2+
3+
export interface Props {
4+
fileId: string;
5+
includeUrl?: boolean;
6+
}
7+
8+
/**
9+
* @title Get File Upload
10+
* @name Get File Upload
11+
* @description Gets a file upload from the assistant
12+
*/
13+
const loader = async (
14+
props: Props,
15+
_req: Request,
16+
ctx: AppContext,
17+
) => {
18+
const response = await ctx.client
19+
["GET /assistant/files/:assistant_name/:assistant_file_id"]({
20+
assistant_name: ctx.assistant,
21+
assistant_file_id: props.fileId,
22+
include_url: props.includeUrl ?? true,
23+
});
24+
25+
const result = await response.json();
26+
return {
27+
success: true,
28+
file: result,
29+
};
30+
};
31+
32+
export default loader;

pinecone-assistant/loaders/list.ts

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import { AppContext } from "../mod.ts";
2+
3+
export interface Props {
4+
filter?: string;
5+
}
6+
7+
/**
8+
* @title List Files
9+
* @name List Files
10+
* @description Lists all files in the assistant
11+
*/
12+
const loader = async (
13+
props: Props,
14+
_req: Request,
15+
ctx: AppContext,
16+
) => {
17+
const response = await ctx.client["GET /assistant/files/:assistant_name"]({
18+
assistant_name: ctx.assistant,
19+
filter: props.filter,
20+
});
21+
22+
const result = await response.json();
23+
return {
24+
success: true,
25+
files: result.files,
26+
};
27+
};
28+
29+
export default loader;

pinecone-assistant/manifest.gen.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,21 @@
22
// This file SHOULD be checked into source version control.
33
// This file is automatically updated during development when running `dev.ts`.
44

5-
import * as $$$$$$$$$0 from "./actions/search.ts";
5+
import * as $$$$$$$$$0 from "./actions/delete.ts";
6+
import * as $$$$$$$$$1 from "./actions/search.ts";
7+
import * as $$$$$$$$$2 from "./actions/upload.ts";
8+
import * as $$$0 from "./loaders/get.ts";
9+
import * as $$$1 from "./loaders/list.ts";
610

711
const manifest = {
12+
"loaders": {
13+
"pinecone-assistant/loaders/get.ts": $$$0,
14+
"pinecone-assistant/loaders/list.ts": $$$1,
15+
},
816
"actions": {
9-
"pinecone-assistant/actions/search.ts": $$$$$$$$$0,
17+
"pinecone-assistant/actions/delete.ts": $$$$$$$$$0,
18+
"pinecone-assistant/actions/search.ts": $$$$$$$$$1,
19+
"pinecone-assistant/actions/upload.ts": $$$$$$$$$2,
1020
},
1121
"name": "pinecone-assistant",
1222
"baseUrl": import.meta.url,

pinecone-assistant/mod.ts

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,6 @@ export default function App(
4747
base: props.host,
4848
headers: new Headers({
4949
"Api-Key": apiKey ?? "",
50-
"Content-Type": "application/json",
5150
"X-Pinecone-API-Version": "2025-01-01",
5251
}),
5352
});

pinecone-assistant/utils/client.ts

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,43 @@
1-
import { ChatContextRequest, ChatContextResponse } from "./types.ts";
1+
import {
2+
ChatContextRequest,
3+
ChatContextResponse,
4+
FileListResponse,
5+
FileUploadResponse,
6+
GetFileUploadResponse,
7+
} from "./types.ts";
28

39
export interface PineconeAPI {
410
"POST /assistant/chat/:assistant_name/context": {
511
response: ChatContextResponse;
12+
searchParams?: {
13+
filter?: Record<string, unknown>;
14+
};
615
body: ChatContextRequest;
716
};
17+
18+
"GET /assistant/files/:assistant_name": {
19+
response: FileListResponse;
20+
searchParams?: {
21+
filter?: string;
22+
};
23+
};
24+
25+
"GET /assistant/files/:assistant_name/:assistant_file_id": {
26+
response: GetFileUploadResponse;
27+
searchParams?: {
28+
include_url?: boolean;
29+
};
30+
};
31+
32+
"DELETE /assistant/files/:assistant_name/:assistant_file_id": {
33+
response: void;
34+
};
35+
36+
"POST /assistant/files/:assistant_name": {
37+
response: FileUploadResponse;
38+
body: FormData;
39+
searchParams?: {
40+
metadata?: string;
41+
};
42+
};
843
}

pinecone-assistant/utils/types.ts

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,50 @@
11
export interface ChatContextRequest {
22
query: string;
3+
filter?: Record<string, unknown>;
4+
}
5+
6+
export interface FileUploadRequest {
7+
form: FormData;
8+
}
9+
10+
type FileStatus =
11+
| "Processing"
12+
| "Available"
13+
| "Deleting"
14+
| "ProcessingFailed"
15+
| "Deleted";
16+
export interface FileUploadResponse {
17+
name: string;
18+
id: string;
19+
metadata: Record<string, unknown> | null;
20+
created_on: string;
21+
updated_on: string;
22+
status: FileStatus;
23+
percent_done: number | null;
24+
signed_url: string | null;
25+
error_message: string | null;
26+
}
27+
28+
export interface GetFileUploadResponse {
29+
name: string;
30+
id: string;
31+
metadata: Record<string, unknown> | null;
32+
created_on: string;
33+
updated_on: string;
34+
status: FileStatus;
35+
percent_done: number | null;
36+
signed_url: string | null;
37+
error_message: string | null;
38+
}
39+
40+
export interface FileListResponse {
41+
files: FileReference[];
342
}
443

544
export interface FileReference {
6-
status: string;
45+
status: FileStatus;
746
id: string;
847
name: string;
9-
size: number;
1048
metadata: null | Record<string, unknown>;
1149
updated_on: string;
1250
created_on: string;
@@ -16,13 +54,13 @@ export interface FileReference {
1654
}
1755

1856
export interface Reference {
19-
type: string;
57+
type: "file";
2058
file: FileReference;
2159
pages: number[];
2260
}
2361

2462
export interface Snippet {
25-
type: string;
63+
type: "file";
2664
content: string;
2765
score: number;
2866
reference: Reference;

0 commit comments

Comments
 (0)