diff --git a/.release-please-manifest.json b/.release-please-manifest.json index b76fc77..ada7355 100644 --- a/.release-please-manifest.json +++ b/.release-please-manifest.json @@ -1,3 +1,3 @@ { - ".": "3.0.0-alpha.27" + ".": "3.1.0" } diff --git a/.stats.yml b/.stats.yml index cac7cf6..ebe1bce 100644 --- a/.stats.yml +++ b/.stats.yml @@ -1,4 +1,4 @@ -configured_endpoints: 12 -openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-new-f181eaeb22a42d197dbd9c45fa61bf9a9b78a91d3334fc0f841494dc73d1a203.yml -openapi_spec_hash: bb8262ebcdea53979cf1cafbc2c68dc8 -config_hash: 9b9291a6c872b063900a46386729ba3c +configured_endpoints: 18 +openapi_spec_url: https://storage.googleapis.com/stainless-sdk-openapi-specs/supermemory--inc%2Fsupermemory-new-b7bf8402df5372a3fac3cb55e083d7c09867328639cb7e2ceb7826de125ccff7.yml +openapi_spec_hash: a91c06034ad9e8a7315927d6f2e18521 +config_hash: 5deef1e3a49e3a7816348fbf7ba259bf diff --git a/CHANGELOG.md b/CHANGELOG.md index e84ab65..377f777 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,21 @@ # Changelog +## 3.1.0 (2025-09-20) + +Full Changelog: [v3.0.0-alpha.27...v3.1.0](https://github.com/supermemoryai/sdk-ts/compare/v3.0.0-alpha.27...v3.1.0) + +### Features + +* **api:** api update ([290de9a](https://github.com/supermemoryai/sdk-ts/commit/290de9a3602efcf37588ffd2ad21f70201d4bd4c)) +* **api:** api update ([6d81959](https://github.com/supermemoryai/sdk-ts/commit/6d8195908aad3dfe3825cb48be754c3fab439248)) +* **api:** manual updates ([08bfcf3](https://github.com/supermemoryai/sdk-ts/commit/08bfcf3eb3b38256b464eee8455a3ea06a016842)) +* **api:** manual updates ([e3de154](https://github.com/supermemoryai/sdk-ts/commit/e3de154f75866468f20ebc89b32d03c16eb1a83f)) + + +### Chores + +* do not install brew dependencies in ./scripts/bootstrap by default ([7484585](https://github.com/supermemoryai/sdk-ts/commit/7484585d8e2972c4ac75c2c593e12eb07f727fb3)) + ## 3.0.0-alpha.27 (2025-09-15) Full Changelog: [v3.0.0-alpha.26...v3.0.0-alpha.27](https://github.com/supermemoryai/sdk-ts/compare/v3.0.0-alpha.26...v3.0.0-alpha.27) diff --git a/README.md b/README.md index 6c80ec1..a857655 100644 --- a/README.md +++ b/README.md @@ -43,12 +43,43 @@ const client = new Supermemory({ apiKey: process.env['SUPERMEMORY_API_KEY'], // This is the default and can be omitted }); -const params: Supermemory.SearchDocumentsParams = { q: 'machine learning concepts' }; -const response: Supermemory.SearchDocumentsResponse = await client.search.documents(params); +const params: Supermemory.MemoryAddParams = { + content: 'This is a detailed article about machine learning concepts...', +}; +const response: Supermemory.MemoryAddResponse = await client.memories.add(params); ``` Documentation for each method, request param, and response field are available in docstrings and will appear on hover in most modern editors. +## File uploads + +Request parameters that correspond to file uploads can be passed in many different forms: + +- `File` (or an object with the same structure) +- a `fetch` `Response` (or an object with the same structure) +- an `fs.ReadStream` +- the return value of our `toFile` helper + +```ts +import fs from 'fs'; +import Supermemory, { toFile } from 'supermemory'; + +const client = new Supermemory(); + +// If you have access to Node `fs` we recommend using `fs.createReadStream()`: +await client.memories.uploadFile({ file: fs.createReadStream('/path/to/file') }); + +// Or if you have the web `File` API you can pass a `File` instance: +await client.memories.uploadFile({ file: new File(['my bytes'], 'file') }); + +// You can also pass a `fetch` `Response`: +await client.memories.uploadFile({ file: await fetch('https://somesite/file') }); + +// Finally, if none of the above are convenient, you can use our `toFile` helper: +await client.memories.uploadFile({ file: await toFile(Buffer.from('my bytes'), 'file') }); +await client.memories.uploadFile({ file: await toFile(new Uint8Array([0, 1, 2]), 'file') }); +``` + ## Handling errors When the library is unable to connect to the API, @@ -57,15 +88,17 @@ a subclass of `APIError` will be thrown: ```ts -const response = await client.search.documents({ q: 'machine learning concepts' }).catch(async (err) => { - if (err instanceof Supermemory.APIError) { - console.log(err.status); // 400 - console.log(err.name); // BadRequestError - console.log(err.headers); // {server: 'nginx', ...} - } else { - throw err; - } -}); +const response = await client.memories + .add({ content: 'This is a detailed article about machine learning concepts...' }) + .catch(async (err) => { + if (err instanceof Supermemory.APIError) { + console.log(err.status); // 400 + console.log(err.name); // BadRequestError + console.log(err.headers); // {server: 'nginx', ...} + } else { + throw err; + } + }); ``` Error codes are as follows: @@ -97,7 +130,7 @@ const client = new Supermemory({ }); // Or, configure per-request: -await client.search.documents({ q: 'machine learning concepts' }, { +await client.memories.add({ content: 'This is a detailed article about machine learning concepts...' }, { maxRetries: 5, }); ``` @@ -114,7 +147,7 @@ const client = new Supermemory({ }); // Override per-request: -await client.search.documents({ q: 'machine learning concepts' }, { +await client.memories.add({ content: 'This is a detailed article about machine learning concepts...' }, { timeout: 5 * 1000, }); ``` @@ -137,15 +170,17 @@ Unlike `.asResponse()` this method consumes the body, returning once it is parse ```ts const client = new Supermemory(); -const response = await client.search.documents({ q: 'machine learning concepts' }).asResponse(); +const response = await client.memories + .add({ content: 'This is a detailed article about machine learning concepts...' }) + .asResponse(); console.log(response.headers.get('X-My-Header')); console.log(response.statusText); // access the underlying Response object -const { data: response, response: raw } = await client.search - .documents({ q: 'machine learning concepts' }) +const { data: response, response: raw } = await client.memories + .add({ content: 'This is a detailed article about machine learning concepts...' }) .withResponse(); console.log(raw.headers.get('X-My-Header')); -console.log(response.results); +console.log(response.id); ``` ### Logging diff --git a/api.md b/api.md index b5c56a4..e5d69ba 100644 --- a/api.md +++ b/api.md @@ -1,5 +1,41 @@ # Memories +Types: + +- MemoryUpdateResponse +- MemoryListResponse +- MemoryAddResponse +- MemoryGetResponse +- MemoryUploadFileResponse + +Methods: + +- client.memories.update(id, { ...params }) -> MemoryUpdateResponse +- client.memories.list({ ...params }) -> MemoryListResponse +- client.memories.delete(id) -> void +- client.memories.add({ ...params }) -> MemoryAddResponse +- client.memories.get(id) -> MemoryGetResponse +- client.memories.uploadFile({ ...params }) -> MemoryUploadFileResponse + +# Documents + +Types: + +- DocumentUpdateResponse +- DocumentListResponse +- DocumentAddResponse +- DocumentGetResponse +- DocumentUploadFileResponse + +Methods: + +- client.documents.update(id, { ...params }) -> DocumentUpdateResponse +- client.documents.list({ ...params }) -> DocumentListResponse +- client.documents.delete(id) -> void +- client.documents.add({ ...params }) -> DocumentAddResponse +- client.documents.get(id) -> DocumentGetResponse +- client.documents.uploadFile({ ...params }) -> DocumentUploadFileResponse + # Search Types: diff --git a/package.json b/package.json index c880852..dd15f56 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "supermemory", - "version": "3.0.0-alpha.27", + "version": "3.1.0", "description": "The official TypeScript library for the Supermemory API", "author": "Supermemory ", "types": "dist/index.d.ts", diff --git a/scripts/bootstrap b/scripts/bootstrap index 062a034..a8b69ff 100755 --- a/scripts/bootstrap +++ b/scripts/bootstrap @@ -4,10 +4,18 @@ set -e cd "$(dirname "$0")/.." -if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ]; then +if [ -f "Brewfile" ] && [ "$(uname -s)" = "Darwin" ] && [ "$SKIP_BREW" != "1" ] && [ -t 0 ]; then brew bundle check >/dev/null 2>&1 || { - echo "==> Installing Homebrew dependencies…" - brew bundle + echo -n "==> Install Homebrew dependencies? (y/N): " + read -r response + case "$response" in + [yY][eE][sS]|[yY]) + brew bundle + ;; + *) + ;; + esac + echo } fi diff --git a/src/client.ts b/src/client.ts index 3602d13..279ba0c 100644 --- a/src/client.ts +++ b/src/client.ts @@ -33,7 +33,30 @@ import { ConnectionListResponse, Connections, } from './resources/connections'; -import { Memories } from './resources/memories'; +import { + DocumentAddParams, + DocumentAddResponse, + DocumentGetResponse, + DocumentListParams, + DocumentListResponse, + DocumentUpdateParams, + DocumentUpdateResponse, + DocumentUploadFileParams, + DocumentUploadFileResponse, + Documents, +} from './resources/documents'; +import { + Memories, + MemoryAddParams, + MemoryAddResponse, + MemoryGetResponse, + MemoryListParams, + MemoryListResponse, + MemoryUpdateParams, + MemoryUpdateResponse, + MemoryUploadFileParams, + MemoryUploadFileResponse, +} from './resources/memories'; import { Search, SearchDocumentsParams, @@ -747,12 +770,14 @@ export class Supermemory { static toFile = Uploads.toFile; memories: API.Memories = new API.Memories(this); + documents: API.Documents = new API.Documents(this); search: API.Search = new API.Search(this); settings: API.Settings = new API.Settings(this); connections: API.Connections = new API.Connections(this); } Supermemory.Memories = Memories; +Supermemory.Documents = Documents; Supermemory.Search = Search; Supermemory.Settings = Settings; Supermemory.Connections = Connections; @@ -760,7 +785,31 @@ Supermemory.Connections = Connections; export declare namespace Supermemory { export type RequestOptions = Opts.RequestOptions; - export { Memories as Memories }; + export { + Memories as Memories, + type MemoryUpdateResponse as MemoryUpdateResponse, + type MemoryListResponse as MemoryListResponse, + type MemoryAddResponse as MemoryAddResponse, + type MemoryGetResponse as MemoryGetResponse, + type MemoryUploadFileResponse as MemoryUploadFileResponse, + type MemoryUpdateParams as MemoryUpdateParams, + type MemoryListParams as MemoryListParams, + type MemoryAddParams as MemoryAddParams, + type MemoryUploadFileParams as MemoryUploadFileParams, + }; + + export { + Documents as Documents, + type DocumentUpdateResponse as DocumentUpdateResponse, + type DocumentListResponse as DocumentListResponse, + type DocumentAddResponse as DocumentAddResponse, + type DocumentGetResponse as DocumentGetResponse, + type DocumentUploadFileResponse as DocumentUploadFileResponse, + type DocumentUpdateParams as DocumentUpdateParams, + type DocumentListParams as DocumentListParams, + type DocumentAddParams as DocumentAddParams, + type DocumentUploadFileParams as DocumentUploadFileParams, + }; export { Search as Search, diff --git a/src/resources/documents.ts b/src/resources/documents.ts new file mode 100644 index 0000000..b7ee359 --- /dev/null +++ b/src/resources/documents.ts @@ -0,0 +1,593 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import { APIResource } from '../core/resource'; +import { APIPromise } from '../core/api-promise'; +import { type Uploadable } from '../core/uploads'; +import { buildHeaders } from '../internal/headers'; +import { RequestOptions } from '../internal/request-options'; +import { multipartFormRequestOptions } from '../internal/uploads'; +import { path } from '../internal/utils/path'; + +export class Documents extends APIResource { + /** + * Update a document with any content type (text, url, file, etc.) and metadata + * + * @example + * ```ts + * const document = await client.documents.update('id'); + * ``` + */ + update( + id: string, + body: DocumentUpdateParams | null | undefined = {}, + options?: RequestOptions, + ): APIPromise { + return this._client.patch(path`/v3/documents/${id}`, { body, ...options }); + } + + /** + * Retrieves a paginated list of documents with their metadata and workflow status + * + * @example + * ```ts + * const documents = await client.documents.list(); + * ``` + */ + list( + body: DocumentListParams | null | undefined = {}, + options?: RequestOptions, + ): APIPromise { + return this._client.post('/v3/documents/list', { body, ...options }); + } + + /** + * Delete a document by ID or customId + * + * @example + * ```ts + * await client.documents.delete('id'); + * ``` + */ + delete(id: string, options?: RequestOptions): APIPromise { + return this._client.delete(path`/v3/documents/${id}`, { + ...options, + headers: buildHeaders([{ Accept: '*/*' }, options?.headers]), + }); + } + + /** + * Add a document with any content type (text, url, file, etc.) and metadata + * + * @example + * ```ts + * const response = await client.documents.add({ + * content: + * 'This is a detailed article about machine learning concepts...', + * }); + * ``` + */ + add(body: DocumentAddParams, options?: RequestOptions): APIPromise { + return this._client.post('/v3/documents', { body, ...options }); + } + + /** + * Get a document by ID + * + * @example + * ```ts + * const document = await client.documents.get('id'); + * ``` + */ + get(id: string, options?: RequestOptions): APIPromise { + return this._client.get(path`/v3/documents/${id}`, options); + } + + /** + * Upload a file to be processed + * + * @example + * ```ts + * const response = await client.documents.uploadFile({ + * file: fs.createReadStream('path/to/file'), + * }); + * ``` + */ + uploadFile( + body: DocumentUploadFileParams, + options?: RequestOptions, + ): APIPromise { + return this._client.post( + '/v3/documents/file', + multipartFormRequestOptions({ body, ...options }, this._client), + ); + } +} + +export interface DocumentUpdateResponse { + id: string; + + status: string; +} + +/** + * List of documents + */ +export interface DocumentListResponse { + memories: Array; + + /** + * Pagination metadata + */ + pagination: DocumentListResponse.Pagination; +} + +export namespace DocumentListResponse { + /** + * Document object + */ + export interface Memory { + /** + * Unique identifier of the document. + */ + id: string; + + /** + * Optional ID of connection the document was created from. This is useful for + * identifying the source of the document. + */ + connectionId: string | null; + + /** + * Creation timestamp + */ + createdAt: string; + + /** + * Optional custom ID of the document. This could be an ID from your database that + * will uniquely identify this document. + */ + customId: string | null; + + /** + * Optional metadata for the document. This is used to store additional information + * about the document. You can use this to store any additional information you + * need about the document. Metadata can be filtered through. Keys must be strings + * and are case sensitive. Values can be strings, numbers, or booleans. You cannot + * nest objects. + */ + metadata: string | number | boolean | { [key: string]: unknown } | Array | null; + + /** + * Status of the document + */ + status: 'unknown' | 'queued' | 'extracting' | 'chunking' | 'embedding' | 'indexing' | 'done' | 'failed'; + + /** + * Summary of the document content + */ + summary: string | null; + + /** + * Title of the document + */ + title: string | null; + + /** + * Type of the document + */ + type: + | 'text' + | 'pdf' + | 'tweet' + | 'google_doc' + | 'google_slide' + | 'google_sheet' + | 'image' + | 'video' + | 'notion_doc' + | 'webpage' + | 'onedrive'; + + /** + * Last update timestamp + */ + updatedAt: string; + + /** + * Optional tags this document should be containerized by. This can be an ID for + * your user, a project ID, or any other identifier you wish to use to group + * documents. + */ + containerTags?: Array; + + /** + * Content of the document (only included when includeContent=true) + */ + content?: string; + } + + /** + * Pagination metadata + */ + export interface Pagination { + currentPage: number; + + limit: number; + + totalItems: number; + + totalPages: number; + } +} + +export interface DocumentAddResponse { + id: string; + + status: string; +} + +/** + * Document object + */ +export interface DocumentGetResponse { + /** + * Unique identifier of the document. + */ + id: string; + + /** + * Optional ID of connection the document was created from. This is useful for + * identifying the source of the document. + */ + connectionId: string | null; + + /** + * The content to extract and process into a document. This can be a URL to a + * website, a PDF, an image, or a video. + * + * Plaintext: Any plaintext format + * + * URL: A URL to a website, PDF, image, or video + * + * We automatically detect the content type from the url's response format. + */ + content: string | null; + + /** + * Creation timestamp + */ + createdAt: string; + + /** + * Optional custom ID of the document. This could be an ID from your database that + * will uniquely identify this document. + */ + customId: string | null; + + /** + * Optional metadata for the document. This is used to store additional information + * about the document. You can use this to store any additional information you + * need about the document. Metadata can be filtered through. Keys must be strings + * and are case sensitive. Values can be strings, numbers, or booleans. You cannot + * nest objects. + */ + metadata: string | number | boolean | { [key: string]: unknown } | Array | null; + + ogImage: string | null; + + /** + * Source of the document + */ + source: string | null; + + /** + * Status of the document + */ + status: 'unknown' | 'queued' | 'extracting' | 'chunking' | 'embedding' | 'indexing' | 'done' | 'failed'; + + /** + * Summary of the document content + */ + summary: string | null; + + summaryEmbeddingModel: string | null; + + summaryEmbeddingModelNew: string | null; + + summaryEmbeddingNew: Array | null; + + /** + * Title of the document + */ + title: string | null; + + /** + * Type of the document + */ + type: + | 'text' + | 'pdf' + | 'tweet' + | 'google_doc' + | 'google_slide' + | 'google_sheet' + | 'image' + | 'video' + | 'notion_doc' + | 'webpage' + | 'onedrive'; + + /** + * Last update timestamp + */ + updatedAt: string; + + /** + * Optional tags this document should be containerized by. This can be an ID for + * your user, a project ID, or any other identifier you wish to use to group + * documents. + */ + containerTags?: Array; + + /** + * Raw content of the document + */ + raw?: null; + + /** + * URL of the document + */ + url?: string | null; +} + +export interface DocumentUploadFileResponse { + id: string; + + status: string; +} + +export interface DocumentUpdateParams { + /** + * Optional tag this document should be containerized by. This can be an ID for + * your user, a project ID, or any other identifier you wish to use to group + * documents. + */ + containerTag?: string; + + /** + * @deprecated (DEPRECATED: Use containerTag instead) Optional tags this document + * should be containerized by. This can be an ID for your user, a project ID, or + * any other identifier you wish to use to group documents. + */ + containerTags?: Array; + + /** + * The content to extract and process into a document. This can be a URL to a + * website, a PDF, an image, or a video. + * + * Plaintext: Any plaintext format + * + * URL: A URL to a website, PDF, image, or video + * + * We automatically detect the content type from the url's response format. + */ + content?: string; + + /** + * Optional custom ID of the document. This could be an ID from your database that + * will uniquely identify this document. + */ + customId?: string; + + /** + * Optional file type override to force specific processing behavior. Valid values: + * text, pdf, tweet, google_doc, google_slide, google_sheet, image, video, + * notion_doc, webpage, onedrive + */ + fileType?: string; + + /** + * Optional metadata for the document. This is used to store additional information + * about the document. You can use this to store any additional information you + * need about the document. Metadata can be filtered through. Keys must be strings + * and are case sensitive. Values can be strings, numbers, or booleans. You cannot + * nest objects. + */ + metadata?: { [key: string]: string | number | boolean | Array }; + + /** + * Required when fileType is 'image' or 'video'. Specifies the exact MIME type to + * use (e.g., 'image/png', 'image/jpeg', 'video/mp4', 'video/webm') + */ + mimeType?: string; +} + +export interface DocumentListParams { + /** + * Optional tags this document should be containerized by. This can be an ID for + * your user, a project ID, or any other identifier you wish to use to group + * documents. + */ + containerTags?: Array; + + /** + * Optional filters to apply to the search. Can be a JSON string or Query object. + */ + filters?: DocumentListParams.Or | DocumentListParams.And; + + /** + * Whether to include the content field in the response. Warning: This can make + * responses significantly larger. + */ + includeContent?: boolean; + + /** + * Number of items per page + */ + limit?: string | number; + + /** + * Sort order + */ + order?: 'asc' | 'desc'; + + /** + * Page number to fetch + */ + page?: string | number; + + /** + * Field to sort by + */ + sort?: 'createdAt' | 'updatedAt'; +} + +export namespace DocumentListParams { + export interface Or { + OR: Array; + } + + export namespace Or { + export interface UnionMember0 { + key: string; + + value: string; + + filterType?: 'metadata' | 'numeric' | 'array_contains'; + + negate?: boolean | 'true' | 'false'; + + numericOperator?: '>' | '<' | '>=' | '<=' | '='; + } + + export interface Or { + OR: Array; + } + + export interface And { + AND: Array; + } + } + + export interface And { + AND: Array; + } + + export namespace And { + export interface UnionMember0 { + key: string; + + value: string; + + filterType?: 'metadata' | 'numeric' | 'array_contains'; + + negate?: boolean | 'true' | 'false'; + + numericOperator?: '>' | '<' | '>=' | '<=' | '='; + } + + export interface Or { + OR: Array; + } + + export interface And { + AND: Array; + } + } +} + +export interface DocumentAddParams { + /** + * The content to extract and process into a document. This can be a URL to a + * website, a PDF, an image, or a video. + * + * Plaintext: Any plaintext format + * + * URL: A URL to a website, PDF, image, or video + * + * We automatically detect the content type from the url's response format. + */ + content: string; + + /** + * Optional tag this document should be containerized by. This can be an ID for + * your user, a project ID, or any other identifier you wish to use to group + * documents. + */ + containerTag?: string; + + /** + * @deprecated (DEPRECATED: Use containerTag instead) Optional tags this document + * should be containerized by. This can be an ID for your user, a project ID, or + * any other identifier you wish to use to group documents. + */ + containerTags?: Array; + + /** + * Optional custom ID of the document. This could be an ID from your database that + * will uniquely identify this document. + */ + customId?: string; + + /** + * Optional file type override to force specific processing behavior. Valid values: + * text, pdf, tweet, google_doc, google_slide, google_sheet, image, video, + * notion_doc, webpage, onedrive + */ + fileType?: string; + + /** + * Optional metadata for the document. This is used to store additional information + * about the document. You can use this to store any additional information you + * need about the document. Metadata can be filtered through. Keys must be strings + * and are case sensitive. Values can be strings, numbers, or booleans. You cannot + * nest objects. + */ + metadata?: { [key: string]: string | number | boolean | Array }; + + /** + * Required when fileType is 'image' or 'video'. Specifies the exact MIME type to + * use (e.g., 'image/png', 'image/jpeg', 'video/mp4', 'video/webm') + */ + mimeType?: string; +} + +export interface DocumentUploadFileParams { + /** + * File to upload and process + */ + file: Uploadable; + + /** + * Optional JSON string of container tags array. This can be an ID for your user, a + * project ID, or any other identifier you wish to use to group documents. + */ + containerTags?: string; + + /** + * Optional file type override to force specific processing behavior. Valid values: + * text, pdf, tweet, google_doc, google_slide, google_sheet, image, video, + * notion_doc, webpage, onedrive + */ + fileType?: string; + + /** + * Required when fileType is 'image' or 'video'. Specifies the exact MIME type to + * use (e.g., 'image/png', 'image/jpeg', 'video/mp4', 'video/webm') + */ + mimeType?: string; +} + +export declare namespace Documents { + export { + type DocumentUpdateResponse as DocumentUpdateResponse, + type DocumentListResponse as DocumentListResponse, + type DocumentAddResponse as DocumentAddResponse, + type DocumentGetResponse as DocumentGetResponse, + type DocumentUploadFileResponse as DocumentUploadFileResponse, + type DocumentUpdateParams as DocumentUpdateParams, + type DocumentListParams as DocumentListParams, + type DocumentAddParams as DocumentAddParams, + type DocumentUploadFileParams as DocumentUploadFileParams, + }; +} diff --git a/src/resources/index.ts b/src/resources/index.ts index 94aa239..7034a0b 100644 --- a/src/resources/index.ts +++ b/src/resources/index.ts @@ -17,7 +17,30 @@ export { type ConnectionImportParams, type ConnectionListDocumentsParams, } from './connections'; -export { Memories } from './memories'; +export { + Documents, + type DocumentUpdateResponse, + type DocumentListResponse, + type DocumentAddResponse, + type DocumentGetResponse, + type DocumentUploadFileResponse, + type DocumentUpdateParams, + type DocumentListParams, + type DocumentAddParams, + type DocumentUploadFileParams, +} from './documents'; +export { + Memories, + type MemoryUpdateResponse, + type MemoryListResponse, + type MemoryAddResponse, + type MemoryGetResponse, + type MemoryUploadFileResponse, + type MemoryUpdateParams, + type MemoryListParams, + type MemoryAddParams, + type MemoryUploadFileParams, +} from './memories'; export { Search, type SearchDocumentsResponse, diff --git a/src/resources/memories.ts b/src/resources/memories.ts index 684b7d9..3675fce 100644 --- a/src/resources/memories.ts +++ b/src/resources/memories.ts @@ -1,5 +1,590 @@ // File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. import { APIResource } from '../core/resource'; +import { APIPromise } from '../core/api-promise'; +import { type Uploadable } from '../core/uploads'; +import { buildHeaders } from '../internal/headers'; +import { RequestOptions } from '../internal/request-options'; +import { multipartFormRequestOptions } from '../internal/uploads'; +import { path } from '../internal/utils/path'; -export class Memories extends APIResource {} +export class Memories extends APIResource { + /** + * Update a document with any content type (text, url, file, etc.) and metadata + * + * @example + * ```ts + * const memory = await client.memories.update('id'); + * ``` + */ + update( + id: string, + body: MemoryUpdateParams | null | undefined = {}, + options?: RequestOptions, + ): APIPromise { + return this._client.patch(path`/v3/documents/${id}`, { body, ...options }); + } + + /** + * Retrieves a paginated list of documents with their metadata and workflow status + * + * @example + * ```ts + * const memories = await client.memories.list(); + * ``` + */ + list( + body: MemoryListParams | null | undefined = {}, + options?: RequestOptions, + ): APIPromise { + return this._client.post('/v3/documents/list', { body, ...options }); + } + + /** + * Delete a document by ID or customId + * + * @example + * ```ts + * await client.memories.delete('id'); + * ``` + */ + delete(id: string, options?: RequestOptions): APIPromise { + return this._client.delete(path`/v3/documents/${id}`, { + ...options, + headers: buildHeaders([{ Accept: '*/*' }, options?.headers]), + }); + } + + /** + * Add a document with any content type (text, url, file, etc.) and metadata + * + * @example + * ```ts + * const response = await client.memories.add({ + * content: + * 'This is a detailed article about machine learning concepts...', + * }); + * ``` + */ + add(body: MemoryAddParams, options?: RequestOptions): APIPromise { + return this._client.post('/v3/documents', { body, ...options }); + } + + /** + * Get a document by ID + * + * @example + * ```ts + * const memory = await client.memories.get('id'); + * ``` + */ + get(id: string, options?: RequestOptions): APIPromise { + return this._client.get(path`/v3/documents/${id}`, options); + } + + /** + * Upload a file to be processed + * + * @example + * ```ts + * const response = await client.memories.uploadFile({ + * file: fs.createReadStream('path/to/file'), + * }); + * ``` + */ + uploadFile(body: MemoryUploadFileParams, options?: RequestOptions): APIPromise { + return this._client.post( + '/v3/documents/file', + multipartFormRequestOptions({ body, ...options }, this._client), + ); + } +} + +export interface MemoryUpdateResponse { + id: string; + + status: string; +} + +/** + * List of documents + */ +export interface MemoryListResponse { + memories: Array; + + /** + * Pagination metadata + */ + pagination: MemoryListResponse.Pagination; +} + +export namespace MemoryListResponse { + /** + * Document object + */ + export interface Memory { + /** + * Unique identifier of the document. + */ + id: string; + + /** + * Optional ID of connection the document was created from. This is useful for + * identifying the source of the document. + */ + connectionId: string | null; + + /** + * Creation timestamp + */ + createdAt: string; + + /** + * Optional custom ID of the document. This could be an ID from your database that + * will uniquely identify this document. + */ + customId: string | null; + + /** + * Optional metadata for the document. This is used to store additional information + * about the document. You can use this to store any additional information you + * need about the document. Metadata can be filtered through. Keys must be strings + * and are case sensitive. Values can be strings, numbers, or booleans. You cannot + * nest objects. + */ + metadata: string | number | boolean | { [key: string]: unknown } | Array | null; + + /** + * Status of the document + */ + status: 'unknown' | 'queued' | 'extracting' | 'chunking' | 'embedding' | 'indexing' | 'done' | 'failed'; + + /** + * Summary of the document content + */ + summary: string | null; + + /** + * Title of the document + */ + title: string | null; + + /** + * Type of the document + */ + type: + | 'text' + | 'pdf' + | 'tweet' + | 'google_doc' + | 'google_slide' + | 'google_sheet' + | 'image' + | 'video' + | 'notion_doc' + | 'webpage' + | 'onedrive'; + + /** + * Last update timestamp + */ + updatedAt: string; + + /** + * Optional tags this document should be containerized by. This can be an ID for + * your user, a project ID, or any other identifier you wish to use to group + * documents. + */ + containerTags?: Array; + + /** + * Content of the document (only included when includeContent=true) + */ + content?: string; + } + + /** + * Pagination metadata + */ + export interface Pagination { + currentPage: number; + + limit: number; + + totalItems: number; + + totalPages: number; + } +} + +export interface MemoryAddResponse { + id: string; + + status: string; +} + +/** + * Document object + */ +export interface MemoryGetResponse { + /** + * Unique identifier of the document. + */ + id: string; + + /** + * Optional ID of connection the document was created from. This is useful for + * identifying the source of the document. + */ + connectionId: string | null; + + /** + * The content to extract and process into a document. This can be a URL to a + * website, a PDF, an image, or a video. + * + * Plaintext: Any plaintext format + * + * URL: A URL to a website, PDF, image, or video + * + * We automatically detect the content type from the url's response format. + */ + content: string | null; + + /** + * Creation timestamp + */ + createdAt: string; + + /** + * Optional custom ID of the document. This could be an ID from your database that + * will uniquely identify this document. + */ + customId: string | null; + + /** + * Optional metadata for the document. This is used to store additional information + * about the document. You can use this to store any additional information you + * need about the document. Metadata can be filtered through. Keys must be strings + * and are case sensitive. Values can be strings, numbers, or booleans. You cannot + * nest objects. + */ + metadata: string | number | boolean | { [key: string]: unknown } | Array | null; + + ogImage: string | null; + + /** + * Source of the document + */ + source: string | null; + + /** + * Status of the document + */ + status: 'unknown' | 'queued' | 'extracting' | 'chunking' | 'embedding' | 'indexing' | 'done' | 'failed'; + + /** + * Summary of the document content + */ + summary: string | null; + + summaryEmbeddingModel: string | null; + + summaryEmbeddingModelNew: string | null; + + summaryEmbeddingNew: Array | null; + + /** + * Title of the document + */ + title: string | null; + + /** + * Type of the document + */ + type: + | 'text' + | 'pdf' + | 'tweet' + | 'google_doc' + | 'google_slide' + | 'google_sheet' + | 'image' + | 'video' + | 'notion_doc' + | 'webpage' + | 'onedrive'; + + /** + * Last update timestamp + */ + updatedAt: string; + + /** + * Optional tags this document should be containerized by. This can be an ID for + * your user, a project ID, or any other identifier you wish to use to group + * documents. + */ + containerTags?: Array; + + /** + * Raw content of the document + */ + raw?: null; + + /** + * URL of the document + */ + url?: string | null; +} + +export interface MemoryUploadFileResponse { + id: string; + + status: string; +} + +export interface MemoryUpdateParams { + /** + * Optional tag this document should be containerized by. This can be an ID for + * your user, a project ID, or any other identifier you wish to use to group + * documents. + */ + containerTag?: string; + + /** + * @deprecated (DEPRECATED: Use containerTag instead) Optional tags this document + * should be containerized by. This can be an ID for your user, a project ID, or + * any other identifier you wish to use to group documents. + */ + containerTags?: Array; + + /** + * The content to extract and process into a document. This can be a URL to a + * website, a PDF, an image, or a video. + * + * Plaintext: Any plaintext format + * + * URL: A URL to a website, PDF, image, or video + * + * We automatically detect the content type from the url's response format. + */ + content?: string; + + /** + * Optional custom ID of the document. This could be an ID from your database that + * will uniquely identify this document. + */ + customId?: string; + + /** + * Optional file type override to force specific processing behavior. Valid values: + * text, pdf, tweet, google_doc, google_slide, google_sheet, image, video, + * notion_doc, webpage, onedrive + */ + fileType?: string; + + /** + * Optional metadata for the document. This is used to store additional information + * about the document. You can use this to store any additional information you + * need about the document. Metadata can be filtered through. Keys must be strings + * and are case sensitive. Values can be strings, numbers, or booleans. You cannot + * nest objects. + */ + metadata?: { [key: string]: string | number | boolean | Array }; + + /** + * Required when fileType is 'image' or 'video'. Specifies the exact MIME type to + * use (e.g., 'image/png', 'image/jpeg', 'video/mp4', 'video/webm') + */ + mimeType?: string; +} + +export interface MemoryListParams { + /** + * Optional tags this document should be containerized by. This can be an ID for + * your user, a project ID, or any other identifier you wish to use to group + * documents. + */ + containerTags?: Array; + + /** + * Optional filters to apply to the search. Can be a JSON string or Query object. + */ + filters?: MemoryListParams.Or | MemoryListParams.And; + + /** + * Whether to include the content field in the response. Warning: This can make + * responses significantly larger. + */ + includeContent?: boolean; + + /** + * Number of items per page + */ + limit?: string | number; + + /** + * Sort order + */ + order?: 'asc' | 'desc'; + + /** + * Page number to fetch + */ + page?: string | number; + + /** + * Field to sort by + */ + sort?: 'createdAt' | 'updatedAt'; +} + +export namespace MemoryListParams { + export interface Or { + OR: Array; + } + + export namespace Or { + export interface UnionMember0 { + key: string; + + value: string; + + filterType?: 'metadata' | 'numeric' | 'array_contains'; + + negate?: boolean | 'true' | 'false'; + + numericOperator?: '>' | '<' | '>=' | '<=' | '='; + } + + export interface Or { + OR: Array; + } + + export interface And { + AND: Array; + } + } + + export interface And { + AND: Array; + } + + export namespace And { + export interface UnionMember0 { + key: string; + + value: string; + + filterType?: 'metadata' | 'numeric' | 'array_contains'; + + negate?: boolean | 'true' | 'false'; + + numericOperator?: '>' | '<' | '>=' | '<=' | '='; + } + + export interface Or { + OR: Array; + } + + export interface And { + AND: Array; + } + } +} + +export interface MemoryAddParams { + /** + * The content to extract and process into a document. This can be a URL to a + * website, a PDF, an image, or a video. + * + * Plaintext: Any plaintext format + * + * URL: A URL to a website, PDF, image, or video + * + * We automatically detect the content type from the url's response format. + */ + content: string; + + /** + * Optional tag this document should be containerized by. This can be an ID for + * your user, a project ID, or any other identifier you wish to use to group + * documents. + */ + containerTag?: string; + + /** + * @deprecated (DEPRECATED: Use containerTag instead) Optional tags this document + * should be containerized by. This can be an ID for your user, a project ID, or + * any other identifier you wish to use to group documents. + */ + containerTags?: Array; + + /** + * Optional custom ID of the document. This could be an ID from your database that + * will uniquely identify this document. + */ + customId?: string; + + /** + * Optional file type override to force specific processing behavior. Valid values: + * text, pdf, tweet, google_doc, google_slide, google_sheet, image, video, + * notion_doc, webpage, onedrive + */ + fileType?: string; + + /** + * Optional metadata for the document. This is used to store additional information + * about the document. You can use this to store any additional information you + * need about the document. Metadata can be filtered through. Keys must be strings + * and are case sensitive. Values can be strings, numbers, or booleans. You cannot + * nest objects. + */ + metadata?: { [key: string]: string | number | boolean | Array }; + + /** + * Required when fileType is 'image' or 'video'. Specifies the exact MIME type to + * use (e.g., 'image/png', 'image/jpeg', 'video/mp4', 'video/webm') + */ + mimeType?: string; +} + +export interface MemoryUploadFileParams { + /** + * File to upload and process + */ + file: Uploadable; + + /** + * Optional JSON string of container tags array. This can be an ID for your user, a + * project ID, or any other identifier you wish to use to group documents. + */ + containerTags?: string; + + /** + * Optional file type override to force specific processing behavior. Valid values: + * text, pdf, tweet, google_doc, google_slide, google_sheet, image, video, + * notion_doc, webpage, onedrive + */ + fileType?: string; + + /** + * Required when fileType is 'image' or 'video'. Specifies the exact MIME type to + * use (e.g., 'image/png', 'image/jpeg', 'video/mp4', 'video/webm') + */ + mimeType?: string; +} + +export declare namespace Memories { + export { + type MemoryUpdateResponse as MemoryUpdateResponse, + type MemoryListResponse as MemoryListResponse, + type MemoryAddResponse as MemoryAddResponse, + type MemoryGetResponse as MemoryGetResponse, + type MemoryUploadFileResponse as MemoryUploadFileResponse, + type MemoryUpdateParams as MemoryUpdateParams, + type MemoryListParams as MemoryListParams, + type MemoryAddParams as MemoryAddParams, + type MemoryUploadFileParams as MemoryUploadFileParams, + }; +} diff --git a/src/resources/search.ts b/src/resources/search.ts index 4d6ca05..24c1e29 100644 --- a/src/resources/search.ts +++ b/src/resources/search.ts @@ -421,9 +421,9 @@ export interface SearchDocumentsParams { documentThreshold?: number; /** - * Optional filters to apply to the search + * Optional filters to apply to the search. Can be a JSON string or Query object. */ - filters?: SearchDocumentsParams.UnionMember0 | { [key: string]: unknown }; + filters?: SearchDocumentsParams.Or | SearchDocumentsParams.And; /** * If true, include full document in the response. This is helpful if you want a @@ -463,10 +463,56 @@ export interface SearchDocumentsParams { } export namespace SearchDocumentsParams { - export interface UnionMember0 { - AND?: Array; + export interface Or { + OR: Array; + } + + export namespace Or { + export interface UnionMember0 { + key: string; + + value: string; + + filterType?: 'metadata' | 'numeric' | 'array_contains'; + + negate?: boolean | 'true' | 'false'; + + numericOperator?: '>' | '<' | '>=' | '<=' | '='; + } + + export interface Or { + OR: Array; + } + + export interface And { + AND: Array; + } + } + + export interface And { + AND: Array; + } + + export namespace And { + export interface UnionMember0 { + key: string; + + value: string; + + filterType?: 'metadata' | 'numeric' | 'array_contains'; + + negate?: boolean | 'true' | 'false'; - OR?: Array; + numericOperator?: '>' | '<' | '>=' | '<=' | '='; + } + + export interface Or { + OR: Array; + } + + export interface And { + AND: Array; + } } } @@ -508,9 +554,9 @@ export interface SearchExecuteParams { documentThreshold?: number; /** - * Optional filters to apply to the search + * Optional filters to apply to the search. Can be a JSON string or Query object. */ - filters?: SearchExecuteParams.UnionMember0 | { [key: string]: unknown }; + filters?: SearchExecuteParams.Or | SearchExecuteParams.And; /** * If true, include full document in the response. This is helpful if you want a @@ -550,10 +596,56 @@ export interface SearchExecuteParams { } export namespace SearchExecuteParams { - export interface UnionMember0 { - AND?: Array; + export interface Or { + OR: Array; + } + + export namespace Or { + export interface UnionMember0 { + key: string; + + value: string; + + filterType?: 'metadata' | 'numeric' | 'array_contains'; + + negate?: boolean | 'true' | 'false'; + + numericOperator?: '>' | '<' | '>=' | '<=' | '='; + } + + export interface Or { + OR: Array; + } + + export interface And { + AND: Array; + } + } + + export interface And { + AND: Array; + } + + export namespace And { + export interface UnionMember0 { + key: string; + + value: string; + + filterType?: 'metadata' | 'numeric' | 'array_contains'; + + negate?: boolean | 'true' | 'false'; + + numericOperator?: '>' | '<' | '>=' | '<=' | '='; + } + + export interface Or { + OR: Array; + } - OR?: Array; + export interface And { + AND: Array; + } } } @@ -570,9 +662,9 @@ export interface SearchMemoriesParams { containerTag?: string; /** - * Optional filters to apply to the search + * Optional filters to apply to the search. Can be a JSON string or Query object. */ - filters?: SearchMemoriesParams.UnionMember0 | { [key: string]: unknown }; + filters?: SearchMemoriesParams.Or | SearchMemoriesParams.And; include?: SearchMemoriesParams.Include; @@ -602,15 +694,68 @@ export interface SearchMemoriesParams { } export namespace SearchMemoriesParams { - export interface UnionMember0 { - AND?: Array; + export interface Or { + OR: Array; + } + + export namespace Or { + export interface UnionMember0 { + key: string; + + value: string; + + filterType?: 'metadata' | 'numeric' | 'array_contains'; + + negate?: boolean | 'true' | 'false'; + + numericOperator?: '>' | '<' | '>=' | '<=' | '='; + } + + export interface Or { + OR: Array; + } + + export interface And { + AND: Array; + } + } + + export interface And { + AND: Array; + } + + export namespace And { + export interface UnionMember0 { + key: string; + + value: string; + + filterType?: 'metadata' | 'numeric' | 'array_contains'; - OR?: Array; + negate?: boolean | 'true' | 'false'; + + numericOperator?: '>' | '<' | '>=' | '<=' | '='; + } + + export interface Or { + OR: Array; + } + + export interface And { + AND: Array; + } } export interface Include { documents?: boolean; + /** + * If true, include forgotten memories in search results. Forgotten memories are + * memories that have been explicitly forgotten or have passed their expiration + * date. + */ + forgottenMemories?: boolean; + relatedMemories?: boolean; summaries?: boolean; diff --git a/src/version.ts b/src/version.ts index 5f2cea0..cc0b2a9 100644 --- a/src/version.ts +++ b/src/version.ts @@ -1 +1 @@ -export const VERSION = '3.0.0-alpha.27'; // x-release-please-version +export const VERSION = '3.1.0'; // x-release-please-version diff --git a/tests/api-resources/documents.test.ts b/tests/api-resources/documents.test.ts new file mode 100644 index 0000000..ac7d566 --- /dev/null +++ b/tests/api-resources/documents.test.ts @@ -0,0 +1,179 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Supermemory, { toFile } from 'supermemory'; + +const client = new Supermemory({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource documents', () => { + // Prism tests are disabled + test.skip('update', async () => { + const responsePromise = client.documents.update('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('update: request options and params are passed correctly', async () => { + // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error + await expect( + client.documents.update( + 'id', + { + containerTag: 'user_123', + containerTags: ['user_123', 'project_123'], + content: 'This is a detailed article about machine learning concepts...', + customId: 'mem_abc123', + fileType: 'pdf', + metadata: { + category: 'technology', + isPublic: true, + readingTime: 5, + source: 'web', + tag_1: 'ai', + tag_2: 'machine-learning', + }, + mimeType: 'image/png', + }, + { path: '/_stainless_unknown_path' }, + ), + ).rejects.toThrow(Supermemory.NotFoundError); + }); + + // Prism tests are disabled + test.skip('list', async () => { + const responsePromise = client.documents.list(); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('list: request options and params are passed correctly', async () => { + // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error + await expect( + client.documents.list( + { + containerTags: ['user_123', 'project_123'], + filters: { + AND: [ + { + key: 'group', + value: 'jira_users', + filterType: 'metadata', + negate: false, + numericOperator: '>', + }, + { + key: 'timestamp', + value: '1742745777', + filterType: 'numeric', + negate: false, + numericOperator: '>', + }, + ], + }, + includeContent: false, + limit: 10, + order: 'desc', + page: 1, + sort: 'createdAt', + }, + { path: '/_stainless_unknown_path' }, + ), + ).rejects.toThrow(Supermemory.NotFoundError); + }); + + // Prism tests are disabled + test.skip('delete', async () => { + const responsePromise = client.documents.delete('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('add: only required params', async () => { + const responsePromise = client.documents.add({ + content: 'This is a detailed article about machine learning concepts...', + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('add: required and optional params', async () => { + const response = await client.documents.add({ + content: 'This is a detailed article about machine learning concepts...', + containerTag: 'user_123', + containerTags: ['user_123', 'project_123'], + customId: 'mem_abc123', + fileType: 'pdf', + metadata: { + category: 'technology', + isPublic: true, + readingTime: 5, + source: 'web', + tag_1: 'ai', + tag_2: 'machine-learning', + }, + mimeType: 'image/png', + }); + }); + + // Prism tests are disabled + test.skip('get', async () => { + const responsePromise = client.documents.get('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('uploadFile: only required params', async () => { + const responsePromise = client.documents.uploadFile({ + file: await toFile(Buffer.from('# my file contents'), 'README.md'), + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('uploadFile: required and optional params', async () => { + const response = await client.documents.uploadFile({ + file: await toFile(Buffer.from('# my file contents'), 'README.md'), + containerTags: '["user_123", "project_123"]', + fileType: 'image', + mimeType: 'image/png', + }); + }); +}); diff --git a/tests/api-resources/memories.test.ts b/tests/api-resources/memories.test.ts new file mode 100644 index 0000000..0504280 --- /dev/null +++ b/tests/api-resources/memories.test.ts @@ -0,0 +1,179 @@ +// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details. + +import Supermemory, { toFile } from 'supermemory'; + +const client = new Supermemory({ + apiKey: 'My API Key', + baseURL: process.env['TEST_API_BASE_URL'] ?? 'http://127.0.0.1:4010', +}); + +describe('resource memories', () => { + // Prism tests are disabled + test.skip('update', async () => { + const responsePromise = client.memories.update('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('update: request options and params are passed correctly', async () => { + // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error + await expect( + client.memories.update( + 'id', + { + containerTag: 'user_123', + containerTags: ['user_123', 'project_123'], + content: 'This is a detailed article about machine learning concepts...', + customId: 'mem_abc123', + fileType: 'pdf', + metadata: { + category: 'technology', + isPublic: true, + readingTime: 5, + source: 'web', + tag_1: 'ai', + tag_2: 'machine-learning', + }, + mimeType: 'image/png', + }, + { path: '/_stainless_unknown_path' }, + ), + ).rejects.toThrow(Supermemory.NotFoundError); + }); + + // Prism tests are disabled + test.skip('list', async () => { + const responsePromise = client.memories.list(); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('list: request options and params are passed correctly', async () => { + // ensure the request options are being passed correctly by passing an invalid HTTP method in order to cause an error + await expect( + client.memories.list( + { + containerTags: ['user_123', 'project_123'], + filters: { + AND: [ + { + key: 'group', + value: 'jira_users', + filterType: 'metadata', + negate: false, + numericOperator: '>', + }, + { + key: 'timestamp', + value: '1742745777', + filterType: 'numeric', + negate: false, + numericOperator: '>', + }, + ], + }, + includeContent: false, + limit: 10, + order: 'desc', + page: 1, + sort: 'createdAt', + }, + { path: '/_stainless_unknown_path' }, + ), + ).rejects.toThrow(Supermemory.NotFoundError); + }); + + // Prism tests are disabled + test.skip('delete', async () => { + const responsePromise = client.memories.delete('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('add: only required params', async () => { + const responsePromise = client.memories.add({ + content: 'This is a detailed article about machine learning concepts...', + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('add: required and optional params', async () => { + const response = await client.memories.add({ + content: 'This is a detailed article about machine learning concepts...', + containerTag: 'user_123', + containerTags: ['user_123', 'project_123'], + customId: 'mem_abc123', + fileType: 'pdf', + metadata: { + category: 'technology', + isPublic: true, + readingTime: 5, + source: 'web', + tag_1: 'ai', + tag_2: 'machine-learning', + }, + mimeType: 'image/png', + }); + }); + + // Prism tests are disabled + test.skip('get', async () => { + const responsePromise = client.memories.get('id'); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('uploadFile: only required params', async () => { + const responsePromise = client.memories.uploadFile({ + file: await toFile(Buffer.from('# my file contents'), 'README.md'), + }); + const rawResponse = await responsePromise.asResponse(); + expect(rawResponse).toBeInstanceOf(Response); + const response = await responsePromise; + expect(response).not.toBeInstanceOf(Response); + const dataAndResponse = await responsePromise.withResponse(); + expect(dataAndResponse.data).toBe(response); + expect(dataAndResponse.response).toBe(rawResponse); + }); + + // Prism tests are disabled + test.skip('uploadFile: required and optional params', async () => { + const response = await client.memories.uploadFile({ + file: await toFile(Buffer.from('# my file contents'), 'README.md'), + containerTags: '["user_123", "project_123"]', + fileType: 'image', + mimeType: 'image/png', + }); + }); +}); diff --git a/tests/api-resources/search.test.ts b/tests/api-resources/search.test.ts index cb4a0c1..38be820 100644 --- a/tests/api-resources/search.test.ts +++ b/tests/api-resources/search.test.ts @@ -31,16 +31,15 @@ describe('resource search', () => { documentThreshold: 0.5, filters: { AND: [ - { key: 'group', negate: false, value: 'jira_users' }, + { key: 'group', value: 'jira_users', filterType: 'metadata', negate: false, numericOperator: '>' }, { - filterType: 'numeric', key: 'timestamp', + value: '1742745777', + filterType: 'numeric', negate: false, numericOperator: '>', - value: '1742745777', }, ], - OR: [{}], }, includeFullDocs: false, includeSummary: false, @@ -74,16 +73,15 @@ describe('resource search', () => { documentThreshold: 0.5, filters: { AND: [ - { key: 'group', negate: false, value: 'jira_users' }, + { key: 'group', value: 'jira_users', filterType: 'metadata', negate: false, numericOperator: '>' }, { - filterType: 'numeric', key: 'timestamp', + value: '1742745777', + filterType: 'numeric', negate: false, numericOperator: '>', - value: '1742745777', }, ], - OR: [{}], }, includeFullDocs: false, includeSummary: false, @@ -113,18 +111,17 @@ describe('resource search', () => { containerTag: 'user_123', filters: { AND: [ - { key: 'group', negate: false, value: 'jira_users' }, + { key: 'group', value: 'jira_users', filterType: 'metadata', negate: false, numericOperator: '>' }, { - filterType: 'numeric', key: 'timestamp', + value: '1742745777', + filterType: 'numeric', negate: false, numericOperator: '>', - value: '1742745777', }, ], - OR: [{}], }, - include: { documents: true, relatedMemories: true, summaries: true }, + include: { documents: true, forgottenMemories: false, relatedMemories: true, summaries: true }, limit: 10, rerank: false, rewriteQuery: false,