@simpleplatform/sdk 1.0.0
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/LICENSE +201 -0
- package/README.md +309 -0
- package/dist/ai.d.ts +239 -0
- package/dist/ai.js +265 -0
- package/dist/build.js +161 -0
- package/dist/graphql.d.ts +33 -0
- package/dist/graphql.js +56 -0
- package/dist/host.d.ts +28 -0
- package/dist/host.js +148 -0
- package/dist/http.d.ts +26 -0
- package/dist/http.js +42 -0
- package/dist/index.d.ts +51 -0
- package/dist/index.js +165 -0
- package/dist/internal/global.d.ts +9 -0
- package/dist/internal/global.js +29 -0
- package/dist/internal/memory.d.ts +103 -0
- package/dist/internal/memory.js +152 -0
- package/dist/internal/polyfills.d.ts +52 -0
- package/dist/internal/polyfills.js +150 -0
- package/dist/security.d.ts +174 -0
- package/dist/security.js +60 -0
- package/dist/settings.d.ts +13 -0
- package/dist/settings.js +25 -0
- package/dist/storage.d.ts +34 -0
- package/dist/storage.js +72 -0
- package/dist/types.d.ts +81 -0
- package/dist/types.js +1 -0
- package/dist/worker-override.js +86 -0
- package/package.json +55 -0
package/dist/ai.d.ts
ADDED
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Simple Platform AI SDK
|
|
3
|
+
*
|
|
4
|
+
* This module provides a high-level, developer-friendly interface for interacting
|
|
5
|
+
* with the platform's powerful, asynchronous AI engine. It abstracts away the
|
|
6
|
+
* underlying complexity of calling the trusted `ai-orchestrator` primitive,
|
|
7
|
+
* handling payload construction and response transformation automatically.
|
|
8
|
+
*
|
|
9
|
+
* This makes AI a first-class, reusable primitive, available to any developer
|
|
10
|
+
* in any logic module they build on the Simple platform.
|
|
11
|
+
*/
|
|
12
|
+
import type { Context, DocumentHandle } from './types';
|
|
13
|
+
/**
|
|
14
|
+
* Base properties common to all JSON Schema definitions.
|
|
15
|
+
*/
|
|
16
|
+
interface JSONSchemaBase {
|
|
17
|
+
description?: string;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* JSON Schema definition for a string type.
|
|
21
|
+
*/
|
|
22
|
+
export interface JSONSchemaString extends JSONSchemaBase {
|
|
23
|
+
format?: 'date' | 'date-time' | 'email' | 'uri';
|
|
24
|
+
maxLength?: number;
|
|
25
|
+
minLength?: number;
|
|
26
|
+
pattern?: string;
|
|
27
|
+
type: 'string';
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* JSON Schema definition for a number or integer type.
|
|
31
|
+
*/
|
|
32
|
+
export interface JSONSchemaNumber extends JSONSchemaBase {
|
|
33
|
+
exclusiveMaximum?: number;
|
|
34
|
+
exclusiveMinimum?: number;
|
|
35
|
+
maximum?: number;
|
|
36
|
+
minimum?: number;
|
|
37
|
+
multipleOf?: number;
|
|
38
|
+
type: 'integer' | 'number';
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* JSON Schema definition for a boolean type.
|
|
42
|
+
*/
|
|
43
|
+
export interface JSONSchemaBoolean extends JSONSchemaBase {
|
|
44
|
+
type: 'boolean';
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* JSON Schema definition for an object type.
|
|
48
|
+
*/
|
|
49
|
+
export interface JSONSchemaObject extends JSONSchemaBase {
|
|
50
|
+
properties: Record<string, JSONSchema>;
|
|
51
|
+
required?: string[];
|
|
52
|
+
type: 'object';
|
|
53
|
+
}
|
|
54
|
+
/**
|
|
55
|
+
* JSON Schema definition for an array type.
|
|
56
|
+
*/
|
|
57
|
+
export interface JSONSchemaArray extends JSONSchemaBase {
|
|
58
|
+
items: JSONSchema;
|
|
59
|
+
maxItems?: number;
|
|
60
|
+
minItems?: number;
|
|
61
|
+
type: 'array';
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* A discriminated union representing a complete and strongly-typed JSON Schema.
|
|
65
|
+
* This provides developers with precise autocompletion and type-checking.
|
|
66
|
+
*/
|
|
67
|
+
export type JSONSchema = JSONSchemaArray | JSONSchemaBoolean | JSONSchemaNumber | JSONSchemaObject | JSONSchemaString;
|
|
68
|
+
/**
|
|
69
|
+
* A set of common configuration options shared across all AI operations.
|
|
70
|
+
* This adheres to the DRY principle, ensuring a consistent API surface.
|
|
71
|
+
*/
|
|
72
|
+
export interface AICommonOptions {
|
|
73
|
+
/**
|
|
74
|
+
* The kind of model to use for this execution.
|
|
75
|
+
* Example: "medium", "large".
|
|
76
|
+
* If omitted, the platform chooses a sensible default model.
|
|
77
|
+
*/
|
|
78
|
+
model?: 'large' | 'lite' | 'medium' | 'xl';
|
|
79
|
+
/** A natural language prompt to guide the AI's process. */
|
|
80
|
+
prompt: string;
|
|
81
|
+
/**
|
|
82
|
+
* If true, forces the AI to provide a detailed, multi-step reasoning
|
|
83
|
+
* process in the generated output. Defaults to false.
|
|
84
|
+
*/
|
|
85
|
+
reasoning?: boolean;
|
|
86
|
+
/**
|
|
87
|
+
* The maximum number of tokens to spend on the reasoning process.
|
|
88
|
+
* If omitted, the platform chooses a sensible default.
|
|
89
|
+
* Ignored if `reasoning` is false.
|
|
90
|
+
*/
|
|
91
|
+
reasoningBudget?: number;
|
|
92
|
+
/**
|
|
93
|
+
* If true, forces the AI to re-process the input, ignoring
|
|
94
|
+
* any previously cached results in the AI Memcache. Defaults to false.
|
|
95
|
+
*/
|
|
96
|
+
regenerate?: boolean;
|
|
97
|
+
/**
|
|
98
|
+
* (Optional) A system prompt to define the AI's role, personality, or
|
|
99
|
+
* high-level instructions for the entire task.
|
|
100
|
+
*/
|
|
101
|
+
systemPrompt?: string;
|
|
102
|
+
/**
|
|
103
|
+
* (Optional) Controls the creativity of the model. A value from 0.0 (most
|
|
104
|
+
* deterministic) to 1.0 (most creative). Defaults to the model's standard.
|
|
105
|
+
*/
|
|
106
|
+
temperature?: number;
|
|
107
|
+
/**
|
|
108
|
+
* (Optional) The maximum time in milliseconds to wait for the AI operation.
|
|
109
|
+
* If this timeout is exceeded, the promise will reject. Defaults to 30,000ms.
|
|
110
|
+
*/
|
|
111
|
+
timeout?: number;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* The configuration options for an AI `extract` operation.
|
|
115
|
+
* Extends the common options with a required `schema`.
|
|
116
|
+
*/
|
|
117
|
+
export interface AIExtractOptions extends AICommonOptions {
|
|
118
|
+
/**
|
|
119
|
+
* The desired JSON schema of the output. This provides a strong contract
|
|
120
|
+
* for the AI to follow when generating its response.
|
|
121
|
+
*/
|
|
122
|
+
schema: JSONSchema;
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* The configuration options for an AI `summarize` operation.
|
|
126
|
+
* Extends the common options. No additional properties are needed.
|
|
127
|
+
*/
|
|
128
|
+
export interface AISummarizeOptions extends AICommonOptions {
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* Configuration options for audio/video transcription.
|
|
132
|
+
* Extends common options but excludes `prompt` as it's generated internally.
|
|
133
|
+
*/
|
|
134
|
+
export interface AITranscribeOptions extends Omit<AICommonOptions, 'prompt'> {
|
|
135
|
+
/**
|
|
136
|
+
* If true, includes timestamps in the transcript (format: [MM:SS]).
|
|
137
|
+
* Only applicable when `includeTranscript` is true.
|
|
138
|
+
*/
|
|
139
|
+
includeTimestamps?: boolean;
|
|
140
|
+
/**
|
|
141
|
+
* If true, includes the full transcript in the response.
|
|
142
|
+
* At least one of `includeTranscript` or `summarize` must be true.
|
|
143
|
+
*/
|
|
144
|
+
includeTranscript?: boolean;
|
|
145
|
+
/**
|
|
146
|
+
* Participant identification configuration:
|
|
147
|
+
* - `true`: Auto-detect participants and label as "Participant 1", "Participant 2", etc.
|
|
148
|
+
* - `string[]`: Array of participant names/roles to identify (e.g., ['Customer', 'Agent'])
|
|
149
|
+
* - `undefined`: No participant identification (default)
|
|
150
|
+
*
|
|
151
|
+
* When provided, the transcript will include participant labels for each segment.
|
|
152
|
+
*/
|
|
153
|
+
participants?: boolean | string[];
|
|
154
|
+
/**
|
|
155
|
+
* If true, includes a summary of the audio/video content.
|
|
156
|
+
* At least one of `includeTranscript` or `summarize` must be true.
|
|
157
|
+
*/
|
|
158
|
+
summarize?: boolean;
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* The response structure from a successful AI `extract` or `summarize` operation.
|
|
162
|
+
*/
|
|
163
|
+
export interface AIExecutionResult {
|
|
164
|
+
/**
|
|
165
|
+
* The primary data returned by the AI. For `extract`, this is the structured
|
|
166
|
+
* object matching the requested schema. For `summarize`, it's the summary string.
|
|
167
|
+
*/
|
|
168
|
+
data: any;
|
|
169
|
+
/**
|
|
170
|
+
* Rich metadata about the AI execution, useful for auditing, debugging, and
|
|
171
|
+
* providing context to the user.
|
|
172
|
+
*/
|
|
173
|
+
metadata: {
|
|
174
|
+
/** The number of tokens in the input prompt. */
|
|
175
|
+
inputTokens: number;
|
|
176
|
+
/** The number of tokens in the generated output. */
|
|
177
|
+
outputTokens: number;
|
|
178
|
+
/** A summary of the AI's internal reasoning process, if provided. */
|
|
179
|
+
reasoning?: string;
|
|
180
|
+
/** The number of tokens used for internal "thinking", if applicable. */
|
|
181
|
+
reasoningTokens?: number;
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
/**
|
|
185
|
+
* Extracts structured data from a given input using the Simple AI engine.
|
|
186
|
+
*
|
|
187
|
+
* @param input The source data for the extraction (string, document handle, or object).
|
|
188
|
+
* @param options The configuration for the extraction operation.
|
|
189
|
+
* @param context The execution context provided by the host.
|
|
190
|
+
* @returns A promise that resolves to an `AIExecutionResult` object.
|
|
191
|
+
* @throws Will throw an error if the operation fails or inputs are invalid.
|
|
192
|
+
*/
|
|
193
|
+
export declare function extract(input: DocumentHandle | object | string, options: AIExtractOptions, context: Context): Promise<AIExecutionResult>;
|
|
194
|
+
/**
|
|
195
|
+
* Generates a summary for a given input using the Simple AI engine.
|
|
196
|
+
*
|
|
197
|
+
* @param input The source data for the summarization (string, document handle, or object).
|
|
198
|
+
* @param options The configuration for the summarization operation.
|
|
199
|
+
* @param context The execution context provided by the host.
|
|
200
|
+
* @returns A promise that resolves to an `AIExecutionResult` object containing the summary.
|
|
201
|
+
* @throws Will throw an error if the operation fails or inputs are invalid.
|
|
202
|
+
*/
|
|
203
|
+
export declare function summarize(input: DocumentHandle | object | string, options: AISummarizeOptions, context: Context): Promise<AIExecutionResult>;
|
|
204
|
+
/**
|
|
205
|
+
* Transcribes audio or video from a document handle using the Simple AI engine.
|
|
206
|
+
*
|
|
207
|
+
* This function internally uses the `extract` operation with a dynamically generated
|
|
208
|
+
* schema based on the requested options. It supports participant identification,
|
|
209
|
+
* timestamps, and summarization.
|
|
210
|
+
*
|
|
211
|
+
* @param input The audio or video file as a DocumentHandle.
|
|
212
|
+
* @param options The configuration for the transcription operation.
|
|
213
|
+
* @param context The execution context provided by the host.
|
|
214
|
+
* @returns A promise that resolves to an `AIExecutionResult` with structured transcription data.
|
|
215
|
+
* @throws Will throw an error if the operation fails or inputs are invalid.
|
|
216
|
+
*
|
|
217
|
+
* @example
|
|
218
|
+
* // Basic transcript
|
|
219
|
+
* const result = await transcribe(audioHandle, { includeTranscript: true }, context)
|
|
220
|
+
* // Returns: { language: "en", transcript: "..." }
|
|
221
|
+
*
|
|
222
|
+
* @example
|
|
223
|
+
* // Transcript with participant identification
|
|
224
|
+
* const result = await transcribe(audioHandle, {
|
|
225
|
+
* includeTranscript: true,
|
|
226
|
+
* participants: ['Customer', 'Support Agent']
|
|
227
|
+
* }, context)
|
|
228
|
+
* // Returns: { language: "en", transcript: "Customer: Hello...\nSupport Agent: Hi...", participants: [...] }
|
|
229
|
+
*
|
|
230
|
+
* @example
|
|
231
|
+
* // Summary with auto-detected participants
|
|
232
|
+
* const result = await transcribe(videoHandle, {
|
|
233
|
+
* summarize: true,
|
|
234
|
+
* participants: true
|
|
235
|
+
* }, context)
|
|
236
|
+
* // Returns: { language: "en", summary: "...", participants: ["Participant 1", "Participant 2"] }
|
|
237
|
+
*/
|
|
238
|
+
export declare function transcribe(input: DocumentHandle, options: AITranscribeOptions, context: Context): Promise<AIExecutionResult>;
|
|
239
|
+
export {};
|
package/dist/ai.js
ADDED
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
import { execute as hostExecute } from './host';
|
|
2
|
+
// ============================================================================
|
|
3
|
+
// Internal Implementation
|
|
4
|
+
// ============================================================================
|
|
5
|
+
/**
|
|
6
|
+
* Recursively processes an object to detect and upload pending files.
|
|
7
|
+
* When a pending DocumentHandle is detected (has `pending: true` and `file_hash`),
|
|
8
|
+
* it calls the ephemeral upload host function and replaces the pending handle
|
|
9
|
+
* with the ephemeral handle returned from the upload.
|
|
10
|
+
*
|
|
11
|
+
* @internal
|
|
12
|
+
*/
|
|
13
|
+
async function _uploadPendingFiles(obj, context) {
|
|
14
|
+
if (obj === null || typeof obj !== 'object') {
|
|
15
|
+
return obj;
|
|
16
|
+
}
|
|
17
|
+
if (obj.pending === true && obj.file_hash) {
|
|
18
|
+
const response = await hostExecute('action:documents/upload-ephemeral', obj, context);
|
|
19
|
+
if (!response.ok) {
|
|
20
|
+
throw new Error(response.error?.message || 'Failed to upload pending file');
|
|
21
|
+
}
|
|
22
|
+
return response.data;
|
|
23
|
+
}
|
|
24
|
+
if (Array.isArray(obj)) {
|
|
25
|
+
return Promise.all(obj.map(item => _uploadPendingFiles(item, context)));
|
|
26
|
+
}
|
|
27
|
+
return obj;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The internal, shared logic for executing any AI operation. This function
|
|
31
|
+
* is not exported and serves as the single, DRY implementation for all
|
|
32
|
+
* public-facing AI functions.
|
|
33
|
+
*
|
|
34
|
+
* @internal
|
|
35
|
+
*/
|
|
36
|
+
async function _executeAIOperation(operation, input, options, context) {
|
|
37
|
+
const { model, prompt, reasoning, reasoningBudget, regenerate = false, systemPrompt, temperature, timeout, } = options;
|
|
38
|
+
const processedInput = await _uploadPendingFiles(input, context);
|
|
39
|
+
// 1. Construct the universal options payload for caching and execution.
|
|
40
|
+
const universalOptions = {
|
|
41
|
+
...(temperature !== undefined && { temperature }),
|
|
42
|
+
...(reasoningBudget !== undefined && { reasoningBudget }),
|
|
43
|
+
...({ reasoning }),
|
|
44
|
+
};
|
|
45
|
+
// 2. Construct the full payload for the `ai-orchestrator` Go primitive.
|
|
46
|
+
// This includes the operation-specific `schema` if it exists.
|
|
47
|
+
const payload = {
|
|
48
|
+
input: processedInput,
|
|
49
|
+
model,
|
|
50
|
+
operation,
|
|
51
|
+
options: universalOptions,
|
|
52
|
+
prompt,
|
|
53
|
+
regenerate,
|
|
54
|
+
schema: options.schema, // Will be undefined for summarize, which is correct
|
|
55
|
+
systemPrompt,
|
|
56
|
+
timeout,
|
|
57
|
+
};
|
|
58
|
+
// 3. Call the trusted primitive. The SDK's `hostExecute` handles the complexity
|
|
59
|
+
// of the underlying `logic:` call and the async execution.
|
|
60
|
+
const response = await hostExecute('logic:dev.simple.system/ai-orchestrator', payload, context);
|
|
61
|
+
if (!response.ok) {
|
|
62
|
+
throw new Error(response.error?.message || `AI '${operation}' operation failed.`);
|
|
63
|
+
}
|
|
64
|
+
// 4. Transform the raw backend response into the clean, developer-facing API contract.
|
|
65
|
+
const aioData = response.data;
|
|
66
|
+
const data = aioData?.data;
|
|
67
|
+
const metadata = aioData?.metadata || {};
|
|
68
|
+
return {
|
|
69
|
+
data,
|
|
70
|
+
metadata: {
|
|
71
|
+
inputTokens: metadata.input_tokens,
|
|
72
|
+
outputTokens: metadata.output_tokens,
|
|
73
|
+
reasoning: metadata.reasoning,
|
|
74
|
+
reasoningTokens: metadata.reasoning_tokens,
|
|
75
|
+
},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
// ============================================================================
|
|
79
|
+
// Public SDK Functions
|
|
80
|
+
// ============================================================================
|
|
81
|
+
/**
|
|
82
|
+
* Extracts structured data from a given input using the Simple AI engine.
|
|
83
|
+
*
|
|
84
|
+
* @param input The source data for the extraction (string, document handle, or object).
|
|
85
|
+
* @param options The configuration for the extraction operation.
|
|
86
|
+
* @param context The execution context provided by the host.
|
|
87
|
+
* @returns A promise that resolves to an `AIExecutionResult` object.
|
|
88
|
+
* @throws Will throw an error if the operation fails or inputs are invalid.
|
|
89
|
+
*/
|
|
90
|
+
export async function extract(input, options, context) {
|
|
91
|
+
// Perform client-side validation specific to the `extract` operation.
|
|
92
|
+
if (!input) {
|
|
93
|
+
throw new Error('The `input` parameter is required for `extract`.');
|
|
94
|
+
}
|
|
95
|
+
if (!options.schema || typeof options.schema !== 'object') {
|
|
96
|
+
throw new Error('The `schema` parameter must be a valid JSONSchema object for `extract`.');
|
|
97
|
+
}
|
|
98
|
+
if (!options.prompt || typeof options.prompt !== 'string') {
|
|
99
|
+
throw new Error('The `prompt` parameter must be a non-empty string for `extract`.');
|
|
100
|
+
}
|
|
101
|
+
// Delegate to the shared internal execution logic.
|
|
102
|
+
return _executeAIOperation('extract', input, options, context);
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* Generates a summary for a given input using the Simple AI engine.
|
|
106
|
+
*
|
|
107
|
+
* @param input The source data for the summarization (string, document handle, or object).
|
|
108
|
+
* @param options The configuration for the summarization operation.
|
|
109
|
+
* @param context The execution context provided by the host.
|
|
110
|
+
* @returns A promise that resolves to an `AIExecutionResult` object containing the summary.
|
|
111
|
+
* @throws Will throw an error if the operation fails or inputs are invalid.
|
|
112
|
+
*/
|
|
113
|
+
export async function summarize(input, options, context) {
|
|
114
|
+
// Perform client-side validation specific to the `summarize` operation.
|
|
115
|
+
if (!input) {
|
|
116
|
+
throw new Error('The `input` parameter is required for `summarize`.');
|
|
117
|
+
}
|
|
118
|
+
if (!options.prompt || typeof options.prompt !== 'string') {
|
|
119
|
+
throw new Error('The `prompt` parameter must be a non-empty string for `summarize`.');
|
|
120
|
+
}
|
|
121
|
+
// Delegate to the shared internal execution logic.
|
|
122
|
+
return _executeAIOperation('summarize', input, options, context);
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Transcribes audio or video from a document handle using the Simple AI engine.
|
|
126
|
+
*
|
|
127
|
+
* This function internally uses the `extract` operation with a dynamically generated
|
|
128
|
+
* schema based on the requested options. It supports participant identification,
|
|
129
|
+
* timestamps, and summarization.
|
|
130
|
+
*
|
|
131
|
+
* @param input The audio or video file as a DocumentHandle.
|
|
132
|
+
* @param options The configuration for the transcription operation.
|
|
133
|
+
* @param context The execution context provided by the host.
|
|
134
|
+
* @returns A promise that resolves to an `AIExecutionResult` with structured transcription data.
|
|
135
|
+
* @throws Will throw an error if the operation fails or inputs are invalid.
|
|
136
|
+
*
|
|
137
|
+
* @example
|
|
138
|
+
* // Basic transcript
|
|
139
|
+
* const result = await transcribe(audioHandle, { includeTranscript: true }, context)
|
|
140
|
+
* // Returns: { language: "en", transcript: "..." }
|
|
141
|
+
*
|
|
142
|
+
* @example
|
|
143
|
+
* // Transcript with participant identification
|
|
144
|
+
* const result = await transcribe(audioHandle, {
|
|
145
|
+
* includeTranscript: true,
|
|
146
|
+
* participants: ['Customer', 'Support Agent']
|
|
147
|
+
* }, context)
|
|
148
|
+
* // Returns: { language: "en", transcript: "Customer: Hello...\nSupport Agent: Hi...", participants: [...] }
|
|
149
|
+
*
|
|
150
|
+
* @example
|
|
151
|
+
* // Summary with auto-detected participants
|
|
152
|
+
* const result = await transcribe(videoHandle, {
|
|
153
|
+
* summarize: true,
|
|
154
|
+
* participants: true
|
|
155
|
+
* }, context)
|
|
156
|
+
* // Returns: { language: "en", summary: "...", participants: ["Participant 1", "Participant 2"] }
|
|
157
|
+
*/
|
|
158
|
+
export async function transcribe(input, options, context) {
|
|
159
|
+
// Validation
|
|
160
|
+
if (!input || typeof input !== 'object' || !input.file_hash) {
|
|
161
|
+
throw new Error('The `input` parameter must be a valid DocumentHandle for `transcribe`.');
|
|
162
|
+
}
|
|
163
|
+
const { includeTimestamps = false, includeTranscript = false, participants, summarize = false, } = options;
|
|
164
|
+
if (!includeTranscript && !summarize) {
|
|
165
|
+
throw new Error('At least one of `includeTranscript` or `summarize` must be true for `transcribe`.');
|
|
166
|
+
}
|
|
167
|
+
// Validate mime type is audio or video
|
|
168
|
+
const mimeType = input.mime_type?.toLowerCase() || '';
|
|
169
|
+
if (!mimeType.startsWith('audio/') && !mimeType.startsWith('video/')) {
|
|
170
|
+
throw new Error('The input file must be an audio or video file for `transcribe`.');
|
|
171
|
+
}
|
|
172
|
+
// Build the dynamic schema based on user options
|
|
173
|
+
const schemaProperties = {
|
|
174
|
+
language: {
|
|
175
|
+
description: 'The detected language of the audio (ISO 639-1 code, e.g., "en", "es")',
|
|
176
|
+
type: 'string',
|
|
177
|
+
},
|
|
178
|
+
};
|
|
179
|
+
const requiredFields = ['language'];
|
|
180
|
+
if (includeTranscript) {
|
|
181
|
+
let transcriptDesc = 'The full transcript of the audio';
|
|
182
|
+
if (participants) {
|
|
183
|
+
if (includeTimestamps) {
|
|
184
|
+
transcriptDesc = 'The full transcript with participant labels and timestamps. Format: [MM:SS] Participant Name: text';
|
|
185
|
+
}
|
|
186
|
+
else {
|
|
187
|
+
transcriptDesc = 'The full transcript with participant labels. Format: Participant Name: text';
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
else if (includeTimestamps) {
|
|
191
|
+
transcriptDesc = 'The full transcript with timestamps. Format: [MM:SS] text';
|
|
192
|
+
}
|
|
193
|
+
schemaProperties.transcript = {
|
|
194
|
+
description: transcriptDesc,
|
|
195
|
+
type: 'string',
|
|
196
|
+
};
|
|
197
|
+
requiredFields.push('transcript');
|
|
198
|
+
}
|
|
199
|
+
if (summarize) {
|
|
200
|
+
schemaProperties.summary = {
|
|
201
|
+
description: participants
|
|
202
|
+
? 'A concise summary of the audio content, including key points from each participant'
|
|
203
|
+
: 'A concise summary of the audio content',
|
|
204
|
+
type: 'string',
|
|
205
|
+
};
|
|
206
|
+
requiredFields.push('summary');
|
|
207
|
+
}
|
|
208
|
+
// Add participants array to schema if participant identification is requested
|
|
209
|
+
if (participants) {
|
|
210
|
+
schemaProperties.participants = {
|
|
211
|
+
description: 'List of identified participants in the audio',
|
|
212
|
+
items: { type: 'string' },
|
|
213
|
+
type: 'array',
|
|
214
|
+
};
|
|
215
|
+
requiredFields.push('participants');
|
|
216
|
+
}
|
|
217
|
+
const schema = {
|
|
218
|
+
properties: schemaProperties,
|
|
219
|
+
required: requiredFields,
|
|
220
|
+
type: 'object',
|
|
221
|
+
};
|
|
222
|
+
// Build the prompt
|
|
223
|
+
let prompt = 'Analyze this audio/video file and provide:\n';
|
|
224
|
+
if (includeTranscript) {
|
|
225
|
+
if (participants) {
|
|
226
|
+
if (Array.isArray(participants)) {
|
|
227
|
+
prompt += `- A complete transcript identifying these participants: ${participants.join(', ')}. `;
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
prompt += '- A complete transcript with participant identification (label participants as Participant 1, Participant 2, etc.). ';
|
|
231
|
+
}
|
|
232
|
+
if (includeTimestamps) {
|
|
233
|
+
prompt += 'Include timestamps in [MM:SS] format before each participant segment.\n';
|
|
234
|
+
}
|
|
235
|
+
else {
|
|
236
|
+
prompt += 'Format each line as "Participant Name: text".\n';
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
else {
|
|
240
|
+
prompt += includeTimestamps
|
|
241
|
+
? '- A complete transcript with timestamps in [MM:SS] format before each segment\n'
|
|
242
|
+
: '- A complete transcript of all spoken content\n';
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
if (summarize) {
|
|
246
|
+
prompt += participants
|
|
247
|
+
? '- A concise summary highlighting key points from each participant\n'
|
|
248
|
+
: '- A concise summary of the main points and key information\n';
|
|
249
|
+
}
|
|
250
|
+
if (participants) {
|
|
251
|
+
if (Array.isArray(participants)) {
|
|
252
|
+
prompt += `- Identify and distinguish between these participants: ${participants.join(', ')}\n`;
|
|
253
|
+
}
|
|
254
|
+
else {
|
|
255
|
+
prompt += '- Identify and list all distinct participants in the audio\n';
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
prompt += '- The detected language code (ISO 639-1 format)\n';
|
|
259
|
+
// Delegate to extract with our generated schema
|
|
260
|
+
return extract(input, {
|
|
261
|
+
...options,
|
|
262
|
+
prompt,
|
|
263
|
+
schema,
|
|
264
|
+
}, context);
|
|
265
|
+
}
|
package/dist/build.js
ADDED
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require('node:fs/promises')
|
|
4
|
+
const os = require('node:os')
|
|
5
|
+
const path = require('node:path')
|
|
6
|
+
const esbuild = require('esbuild')
|
|
7
|
+
|
|
8
|
+
async function main() {
|
|
9
|
+
// eslint-disable-next-line node/prefer-global/process
|
|
10
|
+
const [entryPoint, outFile] = process.argv.slice(2)
|
|
11
|
+
|
|
12
|
+
if (!entryPoint || !outFile) {
|
|
13
|
+
console.error('Usage: simple-sdk-build <entryPoint> <outFile>')
|
|
14
|
+
// eslint-disable-next-line node/prefer-global/process
|
|
15
|
+
process.exit(1)
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const entryPointAbs = path.resolve(entryPoint)
|
|
19
|
+
const outFileAbs = path.resolve(outFile)
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* This is a local esbuild plugin to solve our specific problem.
|
|
23
|
+
* It intercepts module resolution. When it sees a file inside the
|
|
24
|
+
* @simple/sdk package trying to import the exact relative path './host',
|
|
25
|
+
* it redirects the bundler to our worker-override.js script instead.
|
|
26
|
+
* This is the robust way to swap implementations at build time.
|
|
27
|
+
*/
|
|
28
|
+
const simpleSdkHostAliasPlugin = {
|
|
29
|
+
name: 'simple-sdk-host-alias',
|
|
30
|
+
setup(build) {
|
|
31
|
+
// Find the absolute path to the SDK's dist directory.
|
|
32
|
+
// We need this to check if an import is coming from our SDK.
|
|
33
|
+
const sdkDistPath = path.dirname(require.resolve('@simple/sdk'))
|
|
34
|
+
|
|
35
|
+
// The 'onResolve' hook intercepts module lookups.
|
|
36
|
+
build.onResolve({ filter: /^\.\/host$/ }, (args) => {
|
|
37
|
+
// `filter` matches the exact import path string: './host'.
|
|
38
|
+
// `args.importer` is the absolute path of the file doing the import.
|
|
39
|
+
|
|
40
|
+
// If the file doing the import is NOT inside our SDK's dist folder,
|
|
41
|
+
// we ignore it and let esbuild handle it normally.
|
|
42
|
+
if (!args.importer.startsWith(sdkDistPath)) {
|
|
43
|
+
return
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// It's a match! Redirect the import to our override file.
|
|
47
|
+
const overridePath = path.resolve(__dirname, 'worker-override.js')
|
|
48
|
+
return { path: overridePath }
|
|
49
|
+
})
|
|
50
|
+
},
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
// We will now create a temporary entry file that esbuild will use.
|
|
54
|
+
// This allows us to bundle and minify everything in a single, efficient step.
|
|
55
|
+
const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'simple-sdk-build-'))
|
|
56
|
+
const tempEntryFile = path.join(tempDir, 'entry.js')
|
|
57
|
+
|
|
58
|
+
try {
|
|
59
|
+
/**
|
|
60
|
+
* This is the content of our new, dynamic entry point. It controls the
|
|
61
|
+
* precise order of operations.
|
|
62
|
+
*/
|
|
63
|
+
const entryPointContent = `
|
|
64
|
+
// This is the only module we need to define the main handler.
|
|
65
|
+
// The user's code will be bundled into it via the dynamic import.
|
|
66
|
+
|
|
67
|
+
export default async function() {
|
|
68
|
+
const channel = { promise: null };
|
|
69
|
+
|
|
70
|
+
try {
|
|
71
|
+
// STEP 1: Create the promise channel and place it on the global scope.
|
|
72
|
+
globalThis.__SIMPLE_PROMISE_CHANNEL__ = channel;
|
|
73
|
+
|
|
74
|
+
// STEP 2: Dynamically import the user's application. This is a critical change.
|
|
75
|
+
// The 'import()' statement executes the user's top-level code, which will
|
|
76
|
+
// call simple.Handle and populate the channel.promise.
|
|
77
|
+
await import('${entryPointAbs.replace(/\\/g, '/')}');
|
|
78
|
+
|
|
79
|
+
// STEP 3: Now that the user's code has run, check the channel for the promise.
|
|
80
|
+
if (channel.promise) {
|
|
81
|
+
// Await the promise to get the final result of the async handler.
|
|
82
|
+
return await channel.promise;
|
|
83
|
+
} else {
|
|
84
|
+
console.warn('SDK build warning: simple.Handle was not called by the user application.');
|
|
85
|
+
return undefined;
|
|
86
|
+
}
|
|
87
|
+
} finally {
|
|
88
|
+
// STEP 4: Always clean up the global scope to maintain the sandbox.
|
|
89
|
+
delete globalThis.__SIMPLE_PROMISE_CHANNEL__;
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
`
|
|
93
|
+
|
|
94
|
+
await fs.writeFile(tempEntryFile, entryPointContent)
|
|
95
|
+
|
|
96
|
+
// Read the contents of the temp file to pass via stdin.
|
|
97
|
+
const tempFileContents = await fs.readFile(tempEntryFile, 'utf8')
|
|
98
|
+
|
|
99
|
+
// --- STAGE 1: Build the user's application and the wrapper together ---
|
|
100
|
+
const appBundleResult = await esbuild.build({
|
|
101
|
+
bundle: true,
|
|
102
|
+
define: {
|
|
103
|
+
__ASYNC_BUILD__: 'true',
|
|
104
|
+
__IS_WORKER_BUILD__: 'true',
|
|
105
|
+
},
|
|
106
|
+
format: 'iife',
|
|
107
|
+
globalName: '__SIMPLE_MAIN_HANDLER__', // Assign the IIFE result to a global
|
|
108
|
+
minify: true, // Minify the entire bundle
|
|
109
|
+
plugins: [simpleSdkHostAliasPlugin],
|
|
110
|
+
stdin: {
|
|
111
|
+
contents: tempFileContents,
|
|
112
|
+
// eslint-disable-next-line node/prefer-global/process
|
|
113
|
+
resolveDir: process.cwd(),
|
|
114
|
+
sourcefile: 'simple-sdk-virtual-entry.js', // Provide a fake filename for better error messages
|
|
115
|
+
},
|
|
116
|
+
write: false,
|
|
117
|
+
})
|
|
118
|
+
|
|
119
|
+
// The result is a minified IIFE that returns our async handler function.
|
|
120
|
+
const userScriptBundle = appBundleResult.outputFiles[0].text
|
|
121
|
+
|
|
122
|
+
// The final script for the worker just needs to execute this handler.
|
|
123
|
+
// The IIFE returns an object with a `default` property containing our async function.
|
|
124
|
+
const finalWorkerScript = `
|
|
125
|
+
${userScriptBundle}
|
|
126
|
+
return __SIMPLE_MAIN_HANDLER__.default();
|
|
127
|
+
`
|
|
128
|
+
|
|
129
|
+
// --- STAGE 2: Build the final WASM loader with the correctly-built user script injected ---
|
|
130
|
+
await esbuild.build({
|
|
131
|
+
bundle: true,
|
|
132
|
+
define: {
|
|
133
|
+
__ASYNC_BUILD__: 'true',
|
|
134
|
+
// Inject the minified, wrapped script.
|
|
135
|
+
__USER_SCRIPT_BUNDLE__: JSON.stringify(finalWorkerScript),
|
|
136
|
+
},
|
|
137
|
+
minify: true,
|
|
138
|
+
outfile: outFileAbs,
|
|
139
|
+
stdin: {
|
|
140
|
+
contents: `import simple from '@simple/sdk'; simple.Handle(() => {});`,
|
|
141
|
+
// eslint-disable-next-line node/prefer-global/process
|
|
142
|
+
resolveDir: process.cwd(),
|
|
143
|
+
},
|
|
144
|
+
})
|
|
145
|
+
|
|
146
|
+
console.log(`✅ Simple SDK async build successful! Output: ${outFile}`)
|
|
147
|
+
}
|
|
148
|
+
catch (error) {
|
|
149
|
+
console.error('❌ Simple SDK async build failed:')
|
|
150
|
+
console.error(error)
|
|
151
|
+
|
|
152
|
+
// eslint-disable-next-line node/prefer-global/process
|
|
153
|
+
process.exit(1)
|
|
154
|
+
}
|
|
155
|
+
finally {
|
|
156
|
+
// Clean up the temporary directory
|
|
157
|
+
await fs.rm(tempDir, { force: true, recursive: true })
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
main()
|