@agentuity/core 0.0.33 → 0.0.36

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.
Files changed (42) hide show
  1. package/dist/index.js +5 -0
  2. package/dist/index.js.map +1 -0
  3. package/dist/json.js +23 -0
  4. package/dist/json.js.map +1 -0
  5. package/dist/services/_util.js +107 -0
  6. package/dist/services/_util.js.map +1 -0
  7. package/dist/services/adapter.js +2 -0
  8. package/dist/services/adapter.js.map +1 -0
  9. package/dist/services/exception.js +8 -0
  10. package/dist/services/exception.js.map +1 -0
  11. package/dist/services/index.js +8 -0
  12. package/dist/services/index.js.map +1 -0
  13. package/dist/services/keyvalue.js +85 -0
  14. package/dist/services/keyvalue.js.map +1 -0
  15. package/dist/services/objectstore.js +218 -0
  16. package/dist/services/objectstore.js.map +1 -0
  17. package/dist/services/stream.js +392 -0
  18. package/dist/services/stream.js.map +1 -0
  19. package/dist/services/vector.js +242 -0
  20. package/dist/services/vector.js.map +1 -0
  21. package/dist/standard_schema.js +2 -0
  22. package/dist/standard_schema.js.map +1 -0
  23. package/dist/typehelper.js +2 -0
  24. package/dist/typehelper.js.map +1 -0
  25. package/package.json +5 -4
  26. package/src/index.ts +4 -0
  27. package/src/json.ts +26 -0
  28. package/src/services/__test__/keyvalue.test.ts +402 -0
  29. package/src/services/__test__/mock-adapter.ts +114 -0
  30. package/src/services/__test__/objectstore.test.ts +431 -0
  31. package/src/services/__test__/stream.test.ts +554 -0
  32. package/src/services/__test__/vector.test.ts +813 -0
  33. package/src/services/_util.ts +117 -0
  34. package/src/services/adapter.ts +33 -0
  35. package/src/services/exception.ts +7 -0
  36. package/src/services/index.ts +7 -0
  37. package/src/services/keyvalue.ts +185 -0
  38. package/src/services/objectstore.ts +466 -0
  39. package/src/services/stream.ts +614 -0
  40. package/src/services/vector.ts +599 -0
  41. package/src/standard_schema.ts +69 -0
  42. package/src/typehelper.ts +5 -0
@@ -0,0 +1,117 @@
1
+ import { safeStringify } from '../json';
2
+ import type { Body } from './adapter';
3
+ import { ServiceException } from './exception';
4
+
5
+ export const buildUrl = (
6
+ base: string,
7
+ path: string,
8
+ subpath?: string,
9
+ query?: URLSearchParams
10
+ ): string => {
11
+ path = path.startsWith('/') ? path : `/${path}`;
12
+ let url = base.replace(/\/$/, '') + path;
13
+ if (subpath) {
14
+ subpath = subpath.startsWith('/') ? subpath : `/${subpath}`;
15
+ url += subpath;
16
+ }
17
+ if (query) {
18
+ url += `?${query.toString()}`;
19
+ }
20
+ return url;
21
+ };
22
+
23
+ export async function toServiceException(response: Response): Promise<ServiceException> {
24
+ switch (response.status) {
25
+ case 401:
26
+ case 403:
27
+ return new ServiceException('Unauthorized', response.status);
28
+ case 404:
29
+ return new ServiceException('Not Found', response.status);
30
+ default:
31
+ }
32
+ const ct = response.headers.get('content-type');
33
+ if (ct?.includes('json')) {
34
+ try {
35
+ const payload = (await response.json()) as { message?: string; error?: string };
36
+ if (payload.error) {
37
+ return new ServiceException(payload.error, response.status);
38
+ }
39
+ if (payload.message) {
40
+ return new ServiceException(payload.message, response.status);
41
+ }
42
+ return new ServiceException(JSON.stringify(payload), response.status);
43
+ } catch {
44
+ /** don't worry */
45
+ }
46
+ }
47
+ try {
48
+ const body = await response.text();
49
+ return new ServiceException(body, response.status);
50
+ } catch {
51
+ /* fall through */
52
+ }
53
+
54
+ return new ServiceException(response.statusText, response.status);
55
+ }
56
+
57
+ const binaryContentType = 'application/octet-stream';
58
+ const textContentType = 'text/plain';
59
+ const jsonContentType = 'application/json';
60
+
61
+ export async function toPayload(data: unknown): Promise<[Body, string]> {
62
+ if (data === undefined || data === null) {
63
+ return ['', textContentType];
64
+ }
65
+ switch (typeof data) {
66
+ case 'string':
67
+ if (
68
+ (data.charAt(0) === '{' && data.charAt(data.length - 1) === '}') ||
69
+ (data.charAt(0) === '[' && data.charAt(data.length - 1) === ']')
70
+ ) {
71
+ try {
72
+ JSON.parse(data);
73
+ return [data, jsonContentType];
74
+ } catch {
75
+ /* fall through */
76
+ }
77
+ }
78
+ return [data, textContentType];
79
+ case 'boolean':
80
+ return [String(data), textContentType];
81
+ case 'number':
82
+ return [String(data), textContentType];
83
+ case 'object': {
84
+ if (data instanceof ArrayBuffer) {
85
+ return [data, binaryContentType];
86
+ }
87
+ if (data instanceof Buffer) {
88
+ return [data, binaryContentType];
89
+ }
90
+ if (data instanceof ReadableStream) {
91
+ return [data, binaryContentType];
92
+ }
93
+ if (data instanceof Promise) {
94
+ return toPayload(await data);
95
+ }
96
+ if (data instanceof Function) {
97
+ return toPayload(data());
98
+ }
99
+ return [safeStringify(data), jsonContentType];
100
+ }
101
+ }
102
+ return ['', textContentType];
103
+ }
104
+
105
+ export async function fromResponse<T>(response: Response): Promise<T> {
106
+ const contentType = response.headers.get('content-type');
107
+ if (!contentType || contentType?.includes('/json')) {
108
+ return (await response.json()) as T;
109
+ }
110
+ if (contentType?.includes('text/')) {
111
+ return (await response.text()) as T;
112
+ }
113
+ if (contentType?.includes(binaryContentType)) {
114
+ return (await response.arrayBuffer()) as T;
115
+ }
116
+ throw new ServiceException(`Unsupported content-type: ${contentType}`, response.status);
117
+ }
@@ -0,0 +1,33 @@
1
+ export interface FetchSuccessResponse<TData> {
2
+ ok: true;
3
+ data: TData;
4
+ response: Response;
5
+ }
6
+
7
+ export interface FetchErrorResponse {
8
+ ok: false;
9
+ data: never;
10
+ response: Response;
11
+ }
12
+
13
+ export type FetchResponse<T> = FetchErrorResponse | FetchSuccessResponse<T>;
14
+
15
+ export type Body = string | Buffer | ArrayBuffer | ReadableStream;
16
+
17
+ export interface FetchRequest {
18
+ method: 'GET' | 'PUT' | 'POST' | 'DELETE';
19
+ body?: Body;
20
+ signal?: AbortSignal;
21
+ contentType?: string;
22
+ headers?: Record<string, string>;
23
+ telemetry?: {
24
+ name: string;
25
+ attributes?: Record<string, string>;
26
+ };
27
+ binary?: true;
28
+ duplex?: 'half';
29
+ }
30
+
31
+ export interface FetchAdapter {
32
+ invoke<T>(url: string, options: FetchRequest): Promise<FetchResponse<T>>;
33
+ }
@@ -0,0 +1,7 @@
1
+ export class ServiceException extends Error {
2
+ statusCode: number;
3
+ constructor(message: string, statusCode: number) {
4
+ super(message);
5
+ this.statusCode = statusCode;
6
+ }
7
+ }
@@ -0,0 +1,7 @@
1
+ export * from './adapter';
2
+ export * from './exception';
3
+ export * from './keyvalue';
4
+ export * from './objectstore';
5
+ export * from './stream';
6
+ export * from './vector';
7
+ export { buildUrl, toServiceException, toPayload, fromResponse } from './_util';
@@ -0,0 +1,185 @@
1
+ import { FetchAdapter } from './adapter';
2
+ import { buildUrl, toServiceException, toPayload } from './_util';
3
+
4
+ /**
5
+ * the result of a data operation when the data is found
6
+ */
7
+ export interface DataResultFound<T> {
8
+ /**
9
+ * the data from the result of the operation
10
+ */
11
+ data: T;
12
+
13
+ /**
14
+ * the content type of the data
15
+ */
16
+ contentType: string;
17
+
18
+ /**
19
+ * the data was found
20
+ */
21
+ exists: true;
22
+ }
23
+
24
+ /**
25
+ * the result of a data operation when the data is not found
26
+ */
27
+ export interface DataResultNotFound {
28
+ data: never;
29
+ /**
30
+ * the data was not found
31
+ */
32
+ exists: false;
33
+ }
34
+
35
+ /**
36
+ * the result of a data operation
37
+ */
38
+ export type DataResult<T> = DataResultFound<T> | DataResultNotFound;
39
+
40
+ export interface KeyValueStorageSetParams {
41
+ /**
42
+ * the number of milliseconds to keep the value in the cache
43
+ */
44
+ ttl?: number;
45
+ /**
46
+ * the content type of the value
47
+ */
48
+ contentType?: string;
49
+ }
50
+
51
+ export interface KeyValueStorage {
52
+ /**
53
+ * get a value from the key value storage
54
+ *
55
+ * @param name - the name of the key value storage
56
+ * @param key - the key to get the value of
57
+ * @returns the DataResult object
58
+ */
59
+ get<T>(name: string, key: string): Promise<DataResult<T>>;
60
+
61
+ /**
62
+ * set a value in the key value storage
63
+ *
64
+ * @param name - the name of the key value storage
65
+ * @param key - the key to set the value of
66
+ * @param value - the value to set in any of the supported data types
67
+ * @param params - the KeyValueStorageSetParams
68
+ */
69
+ set<T = unknown>(
70
+ name: string,
71
+ key: string,
72
+ value: T,
73
+ params?: KeyValueStorageSetParams
74
+ ): Promise<void>;
75
+
76
+ /**
77
+ * delete a value from the key value storage
78
+ *
79
+ * @param name - the name of the key value storage
80
+ * @param key - the key to delete
81
+ */
82
+ delete(name: string, key: string): Promise<void>;
83
+ }
84
+
85
+ export class KeyValueStorageService implements KeyValueStorage {
86
+ #adapter: FetchAdapter;
87
+ #baseUrl: string;
88
+
89
+ constructor(baseUrl: string, adapter: FetchAdapter) {
90
+ this.#adapter = adapter;
91
+ this.#baseUrl = baseUrl;
92
+ }
93
+
94
+ async get<T>(name: string, key: string): Promise<DataResult<T>> {
95
+ const url = buildUrl(
96
+ this.#baseUrl,
97
+ `/kv/2025-03-17/${encodeURIComponent(name)}/${encodeURIComponent(key)}`
98
+ );
99
+ const signal = AbortSignal.timeout(10_000);
100
+ const res = await this.#adapter.invoke<T>(url, {
101
+ method: 'GET',
102
+ signal,
103
+ telemetry: {
104
+ name: 'agentuity.keyvalue.get',
105
+ attributes: {
106
+ name,
107
+ key,
108
+ },
109
+ },
110
+ });
111
+ if (res.ok) {
112
+ return {
113
+ data: res.data,
114
+ contentType: res.response.headers.get('content-type') ?? 'application/octet-stream',
115
+ exists: true,
116
+ };
117
+ }
118
+ if (res.response.status === 404) {
119
+ return { exists: false } as DataResultNotFound;
120
+ }
121
+ throw await toServiceException(res.response);
122
+ }
123
+
124
+ async set<T = unknown>(
125
+ name: string,
126
+ key: string,
127
+ value: T,
128
+ params?: KeyValueStorageSetParams
129
+ ): Promise<void> {
130
+ let ttlstr = '';
131
+ if (params?.ttl) {
132
+ if (params.ttl < 60) {
133
+ throw new Error(`ttl for keyvalue set must be at least 60 seconds, got ${params.ttl}`);
134
+ }
135
+ ttlstr = `/${params.ttl}`;
136
+ }
137
+ const url = buildUrl(
138
+ this.#baseUrl,
139
+ `/kv/2025-03-17/${encodeURIComponent(name)}/${encodeURIComponent(key)}${ttlstr}`
140
+ );
141
+ const [body, contentType] = await toPayload(value);
142
+ const signal = AbortSignal.timeout(30_000);
143
+ const res = await this.#adapter.invoke<T>(url, {
144
+ method: 'PUT',
145
+ signal,
146
+ body,
147
+ contentType: params?.contentType || contentType,
148
+ telemetry: {
149
+ name: 'agentuity.keyvalue.set',
150
+ attributes: {
151
+ name,
152
+ key,
153
+ ttl: ttlstr,
154
+ },
155
+ },
156
+ });
157
+ if (res.ok) {
158
+ return;
159
+ }
160
+ throw await toServiceException(res.response);
161
+ }
162
+
163
+ async delete(name: string, key: string): Promise<void> {
164
+ const url = buildUrl(
165
+ this.#baseUrl,
166
+ `/kv/2025-03-17/${encodeURIComponent(name)}/${encodeURIComponent(key)}`
167
+ );
168
+ const signal = AbortSignal.timeout(30_000);
169
+ const res = await this.#adapter.invoke(url, {
170
+ method: 'DELETE',
171
+ signal,
172
+ telemetry: {
173
+ name: 'agentuity.keyvalue.delete',
174
+ attributes: {
175
+ name,
176
+ key,
177
+ },
178
+ },
179
+ });
180
+ if (res.ok) {
181
+ return;
182
+ }
183
+ throw await toServiceException(res.response);
184
+ }
185
+ }