@rimori/client 2.5.26-next.1 → 2.5.26-next.3

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.
@@ -0,0 +1,8 @@
1
+ import { Config } from './release';
2
+ /**
3
+ * Read and send the prompts configuration to the release endpoint.
4
+ * Mirrors the pattern of release-db-update.ts.
5
+ * @param config - Configuration object
6
+ * @param release_id - The release ID
7
+ */
8
+ export default function promptsUpload(config: Config, release_id: string): Promise<void>;
@@ -0,0 +1,88 @@
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
10
+ import fs from 'fs';
11
+ import path from 'path';
12
+ import ts from 'typescript';
13
+ /**
14
+ * Read and send the prompts configuration to the release endpoint.
15
+ * Mirrors the pattern of release-db-update.ts.
16
+ * @param config - Configuration object
17
+ * @param release_id - The release ID
18
+ */
19
+ export default function promptsUpload(config, release_id) {
20
+ return __awaiter(this, void 0, void 0, function* () {
21
+ var _a, _b;
22
+ const promptsConfigPath = path.resolve('./rimori/prompts.config.ts');
23
+ // Check if prompts config file exists — optional, skip if not present
24
+ try {
25
+ yield fs.promises.access(promptsConfigPath);
26
+ }
27
+ catch (e) {
28
+ return; // No prompts.config.ts — silently skip
29
+ }
30
+ try {
31
+ // Use TypeScript compiler to transpile and load
32
+ const promptsContent = yield fs.promises.readFile(promptsConfigPath, 'utf8');
33
+ // Transpile TypeScript to JavaScript
34
+ const result = ts.transpile(promptsContent, {
35
+ target: ts.ScriptTarget.ES2020,
36
+ module: ts.ModuleKind.ES2020,
37
+ });
38
+ // Create a temporary file to import the transpiled code
39
+ const tempFile = path.join(process.cwd(), 'temp_prompts_config.js');
40
+ yield fs.promises.writeFile(tempFile, result);
41
+ let prompts;
42
+ try {
43
+ const promptsModule = yield import(`file://${tempFile}`);
44
+ // Collect all named exports as individual prompt definitions
45
+ prompts = Object.values(promptsModule);
46
+ yield fs.promises.unlink(tempFile);
47
+ }
48
+ catch (error) {
49
+ try {
50
+ yield fs.promises.unlink(tempFile);
51
+ }
52
+ catch (e) { }
53
+ throw error;
54
+ }
55
+ if (!Array.isArray(prompts) || prompts.length === 0) {
56
+ console.warn('⚠️ prompts.config.ts has no exports. Skipping.');
57
+ return;
58
+ }
59
+ console.log(`📝 Sending ${prompts.length} prompt definitions...`);
60
+ const requestBody = {
61
+ prompts,
62
+ version: config.version,
63
+ release_channel: config.release_channel,
64
+ plugin_id: config.plugin_id,
65
+ };
66
+ const response = yield fetch(`${config.domain}/release/${release_id}/prompts`, {
67
+ method: 'POST',
68
+ headers: {
69
+ 'Content-Type': 'application/json',
70
+ Authorization: `Bearer ${config.token}`,
71
+ },
72
+ body: JSON.stringify(requestBody),
73
+ });
74
+ if (response.ok) {
75
+ const data = yield response.json();
76
+ console.log(`✅ Prompts uploaded: ${(_b = (_a = data.prompt_names) === null || _a === void 0 ? void 0 : _a.join(', ')) !== null && _b !== void 0 ? _b : 'ok'}`);
77
+ }
78
+ else {
79
+ const text = yield response.text().catch(() => 'unknown error');
80
+ throw new Error(`Failed to upload prompts: ${text}`);
81
+ }
82
+ }
83
+ catch (error) {
84
+ console.error('❌ Error uploading prompts:', error.message);
85
+ throw error;
86
+ }
87
+ });
88
+ }
@@ -23,6 +23,7 @@ import 'dotenv/config';
23
23
  import fs from 'fs';
24
24
  import path from 'path';
25
25
  import dbUpdate from './release-db-update.js';
26
+ import promptsUpload from './release-prompts-upload.js';
26
27
  import { uploadDirectory } from './release-file-upload.js';
27
28
  import { releasePlugin, sendConfiguration } from './release-config-upload.js';
28
29
  // Read version from package.json
@@ -60,8 +61,11 @@ function releaseProcess() {
60
61
  return __awaiter(this, void 0, void 0, function* () {
61
62
  try {
62
63
  console.log(`🚀 Releasing ${config.plugin_id} to ${config.release_channel}...`);
64
+ console.log(`📡 Deploying to: ${config.domain}`);
63
65
  // First send the configuration
64
66
  const release_id = yield sendConfiguration(config);
67
+ // Upload prompts (if prompts.config.ts exists)
68
+ yield promptsUpload(config, release_id);
65
69
  yield dbUpdate(config, release_id);
66
70
  // Then upload the files
67
71
  yield uploadDirectory(config, release_id);
@@ -8,7 +8,15 @@
8
8
  * 2. Add an `updated_at` trigger so the image-sync cron can detect recently
9
9
  * modified entries. The cron derives which columns to scan from release.db_schema.
10
10
  */
11
- type DbColumnType = 'decimal' | 'integer' | 'text' | 'boolean' | 'json' | 'timestamp' | 'uuid' | 'markdown';
11
+ /**
12
+ * 'vector' is stored as `vector(1536)` in the database (pgvector).
13
+ * Marking a column as 'vector' causes the migration system to:
14
+ * 1. Create the column as `vector(1536)` with nullable constraint.
15
+ * 2. Create an IVFFlat index for cosine similarity search.
16
+ * 3. The column MUST be named 'embedding' and only one per table is allowed.
17
+ * 4. Requires `source_column` — the column whose content is embedded async on insert.
18
+ */
19
+ type DbColumnType = 'decimal' | 'integer' | 'text' | 'boolean' | 'json' | 'timestamp' | 'uuid' | 'markdown' | 'vector';
12
20
  /**
13
21
  * Foreign key relationship configuration with cascade delete support.
14
22
  * Defines a relationship where the source record is deleted when the destination record is deleted.
@@ -22,11 +30,11 @@ interface ForeignKeyRelation {
22
30
  on_delete_cascade: boolean;
23
31
  }
24
32
  /**
25
- * Database column definition with support for types, constraints, and relationships.
33
+ * Regular (non-vector) database column definition with support for types, constraints, and relationships.
26
34
  */
27
- export interface DbColumnDefinition {
35
+ interface DbRegularColumnDefinition {
28
36
  /** The data type of the column */
29
- type: DbColumnType;
37
+ type: Exclude<DbColumnType, 'vector'>;
30
38
  /** Human-readable description of the column's purpose */
31
39
  description: string;
32
40
  /** Whether the column can contain null values */
@@ -36,6 +44,8 @@ export interface DbColumnDefinition {
36
44
  /** Default value for the column. can also use sql functions like now(), auth.uid() or gen_random_uuid() */
37
45
  default_value?: string | number | boolean;
38
46
  /** Array of allowed values for enumerated columns */
47
+ /** Not allowed on non-vector columns */
48
+ source_column?: never;
39
49
  /** Foreign key relationship configuration */
40
50
  foreign_key?: ForeignKeyRelation;
41
51
  /** The name of the column before it was renamed. */
@@ -55,6 +65,39 @@ export interface DbColumnDefinition {
55
65
  lang_moderator?: Partial<Omit<DbPermissionDefinition, 'delete'>>;
56
66
  };
57
67
  }
68
+ /**
69
+ * Vector column definition for semantic search via pgvector.
70
+ * The column MUST be named 'embedding' and only one per table is allowed.
71
+ * The backend generates embeddings asynchronously from the source_column content.
72
+ */
73
+ interface DbVectorColumnDefinition {
74
+ /** Must be 'vector' */
75
+ type: 'vector';
76
+ /** Human-readable description of the column's purpose */
77
+ description: string;
78
+ /** Vector columns must be nullable (embeddings are generated asynchronously) */
79
+ nullable: true;
80
+ /** The column whose content is embedded. Must reference an existing column in the same table. */
81
+ source_column: string;
82
+ /** Not applicable to vector columns */
83
+ unique?: never;
84
+ /** Not applicable to vector columns */
85
+ default_value?: never;
86
+ /** Not applicable to vector columns */
87
+ foreign_key?: never;
88
+ /** Not applicable to vector columns */
89
+ old_name?: never;
90
+ /** Not applicable to vector columns */
91
+ deprecated?: never;
92
+ /** Not applicable to vector columns */
93
+ restrict?: never;
94
+ }
95
+ /**
96
+ * Database column definition — discriminated union on `type`.
97
+ * Use `type: 'vector'` for embedding columns (enforces `nullable: true` and requires `source_column`).
98
+ * All other types forbid `source_column`.
99
+ */
100
+ export type DbColumnDefinition = DbRegularColumnDefinition | DbVectorColumnDefinition;
58
101
  /**
59
102
  * Base table structure that all database tables inherit.
60
103
  * Includes standard audit fields for tracking creation and ownership.
@@ -0,0 +1,78 @@
1
+ /**
2
+ * Formatting options for string array variables when rendered into prompts.
3
+ */
4
+ export type StringArrayFormat = 'numberedList' | 'bulletpointList' | 'commaList' | 'newlineList';
5
+ /**
6
+ * Enum variable resolved server-side from a fixed set of values.
7
+ * Supports conditional prompt text per value.
8
+ */
9
+ export interface EnumVariable {
10
+ type: 'enum';
11
+ values: string[];
12
+ conditions?: Record<string, string>;
13
+ }
14
+ /**
15
+ * Numeric variable with min/max bounds.
16
+ * Supports conditional prompt text per value.
17
+ */
18
+ export interface NumberVariable {
19
+ type: 'number';
20
+ min: number;
21
+ max: number;
22
+ conditions?: Record<string, string>;
23
+ }
24
+ /**
25
+ * Free-text string variable with optional prefix/suffix wrappers.
26
+ */
27
+ export interface StringVariable {
28
+ type: 'string';
29
+ pre?: string;
30
+ after?: string;
31
+ }
32
+ /**
33
+ * Array-of-strings variable with optional formatting and prefix/suffix wrappers.
34
+ */
35
+ export interface StringArrayVariable {
36
+ type: 'string[]';
37
+ format?: StringArrayFormat;
38
+ pre?: string;
39
+ after?: string;
40
+ }
41
+ /**
42
+ * UUID variable referencing a backend entity by ID.
43
+ * The backend resolves the UUID to a formatted string via a registered resolver.
44
+ * Safe in system instructions because the resolved content is backend-controlled.
45
+ */
46
+ export interface UuidVariable {
47
+ type: 'uuid';
48
+ /** The resolver name that tells the backend how to look up and format this UUID. */
49
+ resolver: string;
50
+ pre?: string;
51
+ after?: string;
52
+ }
53
+ /**
54
+ * Variables allowed in system instruction blocks.
55
+ */
56
+ export type SystemVariable = EnumVariable | NumberVariable | UuidVariable;
57
+ /**
58
+ * Variables allowed in user instruction blocks (superset of system variables).
59
+ */
60
+ export type UserVariable = SystemVariable | StringVariable | StringArrayVariable;
61
+ /**
62
+ * A block of prompt text with typed template variables.
63
+ */
64
+ export interface InstructionBlock<V> {
65
+ prompt: string;
66
+ variables?: Record<string, V>;
67
+ }
68
+ /**
69
+ * A complete prompt definition uploaded to the backend during release.
70
+ */
71
+ export interface PromptDefinition {
72
+ name: string;
73
+ systemInstructions?: InstructionBlock<SystemVariable>;
74
+ userInstructions?: InstructionBlock<UserVariable>;
75
+ schema?: Record<string, any>;
76
+ tools?: any[];
77
+ model?: string;
78
+ }
@@ -0,0 +1,3 @@
1
+ // Prompt configuration type definitions
2
+ // Used by plugins in their rimori/prompts.config.ts files
3
+ export {};
@@ -161,15 +161,13 @@ export class Translator {
161
161
  if (!this.ai || this.currentLanguage === 'en')
162
162
  return text;
163
163
  const response = yield this.ai.getObject({
164
- systemPrompt: 'You are a translation engine. Return only the translated text.' + additionalInstructions,
165
- userPrompt: `Translate the following text into ${this.currentLanguage}: ${text}`,
166
- cache: true,
167
- responseSchema: {
168
- translation: {
169
- type: 'string',
170
- description: `The translation of the input text into ${this.currentLanguage}.`,
171
- },
164
+ prompt: 'global.translator.translate',
165
+ variables: {
166
+ additionalInstructions: additionalInstructions !== null && additionalInstructions !== void 0 ? additionalInstructions : '',
167
+ language: this.currentLanguage,
168
+ text,
172
169
  },
170
+ cache: true,
173
171
  });
174
172
  const translation = response === null || response === void 0 ? void 0 : response.translation;
175
173
  if (translation) {
package/dist/index.d.ts CHANGED
@@ -3,6 +3,7 @@ export * from './plugin/RimoriClient';
3
3
  export * from './fromRimori/PluginTypes';
4
4
  export * from './plugin/StandaloneClient';
5
5
  export * from './cli/types/DatabaseTypes';
6
+ export * from './cli/types/PromptTypes';
6
7
  export * from './plugin/TTS/MessageSender';
7
8
  export * from './utils/difficultyConverter';
8
9
  export * from './plugin/CommunicationHandler';
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ export * from './plugin/RimoriClient';
4
4
  export * from './fromRimori/PluginTypes';
5
5
  export * from './plugin/StandaloneClient';
6
6
  export * from './cli/types/DatabaseTypes';
7
+ export * from './cli/types/PromptTypes';
7
8
  export * from './plugin/TTS/MessageSender';
8
9
  export * from './utils/difficultyConverter';
9
10
  export * from './plugin/CommunicationHandler';
@@ -31,7 +31,7 @@ export class RimoriClient {
31
31
  };
32
32
  this.rimoriInfo = info;
33
33
  this.sharedContent = new SharedContentController(supabase, this);
34
- this.ai = new AIModule(info.backendUrl, () => this.rimoriInfo.token);
34
+ this.ai = new AIModule(info.backendUrl, () => this.rimoriInfo.token, info.pluginId);
35
35
  this.ai.setOnRateLimited((exercisesRemaining) => {
36
36
  EventBus.emit(info.pluginId, 'global.quota.triggerExceeded', { exercises_remaining: exercisesRemaining });
37
37
  });
@@ -44,11 +44,13 @@ export interface ObjectRequest {
44
44
  * High level instructions for the AI to follow. Behaviour, tone, restrictions, etc.
45
45
  * Example: "Act like a recipe writer."
46
46
  */
47
+ /** @deprecated Use server-side prompt definitions (prompt + variables) instead of building requests client-side. */
47
48
  behaviour?: string;
48
49
  /**
49
50
  * The specific instruction for the AI to follow.
50
51
  * Example: "Generate a recipe using chicken, rice and vegetables."
51
52
  */
53
+ /** @deprecated Use server-side prompt definitions (prompt + variables) instead of building requests client-side. */
52
54
  instructions: string;
53
55
  }
54
56
  /**
@@ -58,9 +60,16 @@ export interface ObjectRequest {
58
60
  export declare class AIModule {
59
61
  private getToken;
60
62
  private backendUrl;
63
+ private pluginId;
61
64
  private sessionTokenId;
62
65
  private onRateLimitedCb?;
63
- constructor(backendUrl: string, getToken: () => string);
66
+ constructor(backendUrl: string, getToken: () => string, pluginId?: string);
67
+ /**
68
+ * Resolves a prompt name following the event naming convention:
69
+ * - 2-segment names (e.g. 'storytelling.story') get prefixed with pluginId → '<pluginId>.storytelling.story'
70
+ * - 3+ segment names starting with 'global.' (e.g. 'global.translator.translate') are sent as-is
71
+ */
72
+ private resolvePromptName;
64
73
  /** Exercise session management. */
65
74
  readonly session: {
66
75
  /** Returns the current exercise session token ID (null if no active session). */
@@ -94,7 +103,9 @@ export declare class AIModule {
94
103
  * @param cache Whether to cache the result (default: false).
95
104
  * @param model The model to use for generation.
96
105
  */
97
- getSteamedText(messages: Message[], onMessage: OnLLMResponse, tools?: Tool[], cache?: boolean, model?: string, knowledgeId?: string): Promise<string>;
106
+ getSteamedText(messages: Message[], onMessage: OnLLMResponse, tools?: Tool[], cache?: boolean, model?: string,
107
+ /** @deprecated Use uuid variable with resolver 'knowledgeEntry' in prompt definitions instead. */
108
+ knowledgeId?: string, prompt?: string, variables?: Record<string, any>): Promise<string>;
98
109
  /**
99
110
  * Generate voice audio from text using AI.
100
111
  * @param text The text to convert to voice.
@@ -112,48 +123,56 @@ export declare class AIModule {
112
123
  * @returns The transcribed text.
113
124
  */
114
125
  getTextFromVoice(file: Blob, language?: Language): Promise<string>;
126
+ /** @deprecated Used by legacy client-side prompt path. Will be removed once all plugins migrate to server-side prompt definitions. */
115
127
  private getChatMessage;
116
128
  /**
117
129
  * Generate a structured object from a request using AI.
118
- * @param request The object generation request.
119
- * @param request.systemPrompt The system prompt to use for generation.
120
- * @param request.responseSchema The response schema to use for generation.
121
- * @param request.userPrompt The user prompt to use for generation.
122
130
  * @param request.cache Whether to cache the result (default: false).
123
131
  * @param request.tools The tools to use for generation.
124
132
  * @param request.model The model to use for generation.
133
+ * @param request.prompt Server-side prompt name (e.g. 'writing.analysis').
134
+ * @param request.variables Variables for the server-side prompt template.
125
135
  * @returns The generated object.
126
136
  */
127
137
  getObject<T = any>(params: {
128
- systemPrompt: string;
129
- responseSchema: AIObjectTool;
138
+ /** @deprecated Use prompt + variables instead of client-side system prompts. */
139
+ systemPrompt?: string;
140
+ /** @deprecated Use prompt + variables instead. Schema is loaded server-side from the prompt definition. */
141
+ responseSchema?: AIObjectTool;
142
+ /** @deprecated Use prompt + variables instead of client-side user prompts. */
130
143
  userPrompt?: string;
131
144
  cache?: boolean;
132
145
  tools?: Tool[];
133
146
  model?: string;
147
+ /** @deprecated Use uuid variable with resolver 'knowledgeEntry' in prompt definitions instead. */
134
148
  knowledgeId?: string;
149
+ prompt?: string;
150
+ variables?: Record<string, any>;
135
151
  }): Promise<T>;
136
152
  /**
137
153
  * Generate a streamed structured object from a request using AI.
138
- * @param request The object generation request.
139
- * @param request.systemPrompt The system prompt to use for generation.
140
- * @param request.responseSchema The response schema to use for generation.
141
- * @param request.userPrompt The user prompt to use for generation.
142
154
  * @param request.onResult Callback for each result chunk.
143
155
  * @param request.cache Whether to cache the result (default: false).
144
156
  * @param request.tools The tools to use for generation.
145
157
  * @param request.model The model to use for generation.
146
- * @param request.knowledgeId Optional knowledge entry ID to ground AI content in real facts.
158
+ * @param request.prompt Server-side prompt name (e.g. 'writing.analysis').
159
+ * @param request.variables Variables for the server-side prompt template.
147
160
  */
148
161
  getStreamedObject<T = any>(params: {
149
- systemPrompt: string;
150
- responseSchema: AIObjectTool;
162
+ /** @deprecated Use prompt + variables instead of client-side system prompts. */
163
+ systemPrompt?: string;
164
+ /** @deprecated Use prompt + variables instead. Schema is loaded server-side from the prompt definition. */
165
+ responseSchema?: AIObjectTool;
166
+ /** @deprecated Use prompt + variables instead of client-side user prompts. */
151
167
  userPrompt?: string;
152
168
  onResult: OnStreamedObjectResult<T>;
153
169
  cache?: boolean;
154
170
  tools?: Tool[];
155
171
  model?: string;
172
+ /** @deprecated Use uuid variable with resolver 'knowledgeEntry' in prompt definitions instead. */
156
173
  knowledgeId?: string;
174
+ prompt?: string;
175
+ variables?: Record<string, any>;
157
176
  }): Promise<T>;
158
177
  private streamObject;
159
178
  private sendToolResult;
@@ -12,7 +12,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
12
12
  * Provides access to text generation, voice synthesis, and object generation.
13
13
  */
14
14
  export class AIModule {
15
- constructor(backendUrl, getToken) {
15
+ constructor(backendUrl, getToken, pluginId) {
16
16
  this.sessionTokenId = null;
17
17
  /** Exercise session management. */
18
18
  this.session = {
@@ -53,6 +53,21 @@ export class AIModule {
53
53
  };
54
54
  this.backendUrl = backendUrl;
55
55
  this.getToken = getToken;
56
+ this.pluginId = pluginId;
57
+ }
58
+ /**
59
+ * Resolves a prompt name following the event naming convention:
60
+ * - 2-segment names (e.g. 'storytelling.story') get prefixed with pluginId → '<pluginId>.storytelling.story'
61
+ * - 3+ segment names starting with 'global.' (e.g. 'global.translator.translate') are sent as-is
62
+ */
63
+ resolvePromptName(name) {
64
+ if (name.startsWith('global.'))
65
+ return name;
66
+ const segments = name.split('.');
67
+ if (segments.length === 2 && this.pluginId) {
68
+ return `${this.pluginId}.${name}`;
69
+ }
70
+ return name;
56
71
  }
57
72
  /** Registers a callback invoked whenever a 429 rate-limit response is received. */
58
73
  setOnRateLimited(cb) {
@@ -73,11 +88,6 @@ export class AIModule {
73
88
  tools,
74
89
  model,
75
90
  messages,
76
- responseSchema: {
77
- result: {
78
- type: 'string',
79
- },
80
- },
81
91
  });
82
92
  return result;
83
93
  });
@@ -91,7 +101,9 @@ export class AIModule {
91
101
  * @param model The model to use for generation.
92
102
  */
93
103
  getSteamedText(messages_1, onMessage_1, tools_1) {
94
- return __awaiter(this, arguments, void 0, function* (messages, onMessage, tools, cache = false, model, knowledgeId) {
104
+ return __awaiter(this, arguments, void 0, function* (messages, onMessage, tools, cache = false, model,
105
+ /** @deprecated Use uuid variable with resolver 'knowledgeEntry' in prompt definitions instead. */
106
+ knowledgeId, prompt, variables) {
95
107
  const messageId = Math.random().toString(36).substring(3);
96
108
  const { result } = yield this.streamObject({
97
109
  cache,
@@ -99,11 +111,8 @@ export class AIModule {
99
111
  model,
100
112
  messages,
101
113
  knowledgeId,
102
- responseSchema: {
103
- result: {
104
- type: 'string',
105
- },
106
- },
114
+ prompt,
115
+ variables,
107
116
  onResult: ({ result }) => onMessage(messageId, result, false),
108
117
  });
109
118
  onMessage(messageId, result, true);
@@ -129,7 +138,15 @@ export class AIModule {
129
138
  'Content-Type': 'application/json',
130
139
  Authorization: `Bearer ${this.getToken()}`,
131
140
  },
132
- body: JSON.stringify({ input: text, voice, speed, language, cache, instructions, session_token_id: (_a = this.sessionTokenId) !== null && _a !== void 0 ? _a : undefined }),
141
+ body: JSON.stringify({
142
+ input: text,
143
+ voice,
144
+ speed,
145
+ language,
146
+ cache,
147
+ instructions,
148
+ session_token_id: (_a = this.sessionTokenId) !== null && _a !== void 0 ? _a : undefined,
149
+ }),
133
150
  }).then((r) => r.blob());
134
151
  });
135
152
  }
@@ -162,6 +179,7 @@ export class AIModule {
162
179
  });
163
180
  });
164
181
  }
182
+ /** @deprecated Used by legacy client-side prompt path. Will be removed once all plugins migrate to server-side prompt definitions. */
165
183
  getChatMessage(systemPrompt, userPrompt) {
166
184
  const messages = [{ role: 'system', content: systemPrompt }];
167
185
  if (userPrompt) {
@@ -171,70 +189,75 @@ export class AIModule {
171
189
  }
172
190
  /**
173
191
  * Generate a structured object from a request using AI.
174
- * @param request The object generation request.
175
- * @param request.systemPrompt The system prompt to use for generation.
176
- * @param request.responseSchema The response schema to use for generation.
177
- * @param request.userPrompt The user prompt to use for generation.
178
192
  * @param request.cache Whether to cache the result (default: false).
179
193
  * @param request.tools The tools to use for generation.
180
194
  * @param request.model The model to use for generation.
195
+ * @param request.prompt Server-side prompt name (e.g. 'writing.analysis').
196
+ * @param request.variables Variables for the server-side prompt template.
181
197
  * @returns The generated object.
182
198
  */
183
199
  getObject(params) {
184
200
  return __awaiter(this, void 0, void 0, function* () {
185
- const { systemPrompt, responseSchema, userPrompt, cache = false, tools = [], model = undefined, knowledgeId, } = params;
201
+ const { systemPrompt, responseSchema, userPrompt, cache = false, tools = [], model = undefined, knowledgeId, prompt, variables, } = params;
186
202
  return yield this.streamObject({
187
203
  responseSchema,
188
- messages: this.getChatMessage(systemPrompt, userPrompt),
204
+ messages: systemPrompt ? this.getChatMessage(systemPrompt, userPrompt) : [],
189
205
  cache,
190
206
  tools,
191
207
  model,
192
208
  knowledgeId,
209
+ prompt,
210
+ variables,
193
211
  });
194
212
  });
195
213
  }
196
214
  /**
197
215
  * Generate a streamed structured object from a request using AI.
198
- * @param request The object generation request.
199
- * @param request.systemPrompt The system prompt to use for generation.
200
- * @param request.responseSchema The response schema to use for generation.
201
- * @param request.userPrompt The user prompt to use for generation.
202
216
  * @param request.onResult Callback for each result chunk.
203
217
  * @param request.cache Whether to cache the result (default: false).
204
218
  * @param request.tools The tools to use for generation.
205
219
  * @param request.model The model to use for generation.
206
- * @param request.knowledgeId Optional knowledge entry ID to ground AI content in real facts.
220
+ * @param request.prompt Server-side prompt name (e.g. 'writing.analysis').
221
+ * @param request.variables Variables for the server-side prompt template.
207
222
  */
208
223
  getStreamedObject(params) {
209
224
  return __awaiter(this, void 0, void 0, function* () {
210
- const { systemPrompt, responseSchema, userPrompt, onResult, cache = false, tools = [], model = undefined, knowledgeId, } = params;
225
+ const { systemPrompt, responseSchema, userPrompt, onResult, cache = false, tools = [], model = undefined, knowledgeId, prompt, variables, } = params;
211
226
  return yield this.streamObject({
212
227
  responseSchema,
213
- messages: this.getChatMessage(systemPrompt, userPrompt),
228
+ messages: systemPrompt ? this.getChatMessage(systemPrompt, userPrompt) : [],
214
229
  onResult,
215
230
  cache,
216
231
  tools,
217
232
  model,
218
233
  knowledgeId,
234
+ prompt,
235
+ variables,
219
236
  });
220
237
  });
221
238
  }
222
239
  streamObject(params) {
223
240
  return __awaiter(this, void 0, void 0, function* () {
224
241
  var _a, _b, _c, _d, _e;
225
- const { messages, responseSchema, onResult = () => null, cache = false, tools = [], model = undefined, knowledgeId, } = params;
242
+ const { messages, responseSchema, onResult = () => null, cache = false, tools = [], model = undefined, knowledgeId, prompt, variables, } = params;
226
243
  const chatMessages = messages.map((message, index) => (Object.assign(Object.assign({}, message), { id: `${index + 1}` })));
244
+ const payload = {
245
+ cache,
246
+ tools,
247
+ stream: true,
248
+ messages: chatMessages,
249
+ model,
250
+ knowledge_id: knowledgeId,
251
+ session_token_id: (_a = this.sessionTokenId) !== null && _a !== void 0 ? _a : undefined,
252
+ };
253
+ if (prompt) {
254
+ payload.prompt = { name: this.resolvePromptName(prompt), variables: variables !== null && variables !== void 0 ? variables : {} };
255
+ }
256
+ if (responseSchema) {
257
+ payload.responseSchema = responseSchema;
258
+ }
227
259
  const response = yield fetch(`${this.backendUrl}/ai/llm`, {
228
- body: JSON.stringify({
229
- cache,
230
- tools,
231
- stream: true,
232
- responseSchema,
233
- messages: chatMessages,
234
- model,
235
- knowledge_id: knowledgeId,
236
- session_token_id: (_a = this.sessionTokenId) !== null && _a !== void 0 ? _a : undefined,
237
- }),
260
+ body: JSON.stringify(payload),
238
261
  method: 'POST',
239
262
  headers: { Authorization: `Bearer ${this.getToken()}`, 'Content-Type': 'application/json' },
240
263
  });
@@ -300,6 +323,21 @@ export class AIModule {
300
323
  }
301
324
  continue;
302
325
  }
326
+ // Handle debug: line (prompt resolution debug info, dev/local only)
327
+ if (line.startsWith('debug:')) {
328
+ try {
329
+ const debug = JSON.parse(line.slice(6).trim());
330
+ console.group(`[Rimori Prompt] ${debug.promptName}`);
331
+ console.log('System prompt:\n', debug.system);
332
+ console.log('User prompt:\n', debug.user);
333
+ console.log('Variables:', debug.variables);
334
+ console.groupEnd();
335
+ }
336
+ catch (_g) {
337
+ // Ignore malformed debug lines
338
+ }
339
+ continue;
340
+ }
303
341
  const command = line.substring(0, 5);
304
342
  const dataStr = line.substring(5).trim();
305
343
  if (dataStr === '[DONE]') {
@@ -25,6 +25,21 @@ export type PublicityLevel = 'own' | 'guild' | 'lang';
25
25
  * Database module for plugin database operations.
26
26
  * Provides access to plugin tables with automatic prefixing and schema management.
27
27
  */
28
+ export interface VectorSearchParams {
29
+ /** Table name without plugin prefix (e.g. 'pages') */
30
+ tableName: string;
31
+ /** The text query to search for */
32
+ query: string;
33
+ /** Maximum number of results (default: 5) */
34
+ limit?: number;
35
+ /** Similarity threshold 0-1 (default: 0.5) */
36
+ threshold?: number;
37
+ /** Which columns to return (default: all) */
38
+ selectColumns?: string[];
39
+ }
40
+ export type VectorSearchResult<T = Record<string, unknown>> = Array<T & {
41
+ similarity: number;
42
+ }>;
28
43
  export declare class DbModule {
29
44
  private supabase;
30
45
  private rimoriInfo;
@@ -63,5 +78,12 @@ export declare class DbModule {
63
78
  * @param publicity The desired publicity level
64
79
  */
65
80
  setPublicity(table: string, entryId: string, publicity: PublicityLevel): Promise<void>;
81
+ /**
82
+ * Search a plugin table using vector similarity (cosine distance).
83
+ * The table must have a vector column named 'embedding' defined in db.config.ts.
84
+ * @param params Search parameters
85
+ * @returns Matching rows sorted by similarity
86
+ */
87
+ vectorSearch<T = Record<string, unknown>>(params: VectorSearchParams): Promise<VectorSearchResult<T>>;
66
88
  }
67
89
  export {};
@@ -7,10 +7,6 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
7
7
  step((generator = generator.apply(thisArg, _arguments || [])).next());
8
8
  });
9
9
  };
10
- /**
11
- * Database module for plugin database operations.
12
- * Provides access to plugin tables with automatic prefixing and schema management.
13
- */
14
10
  export class DbModule {
15
11
  constructor(supabase, communicationHandler, info) {
16
12
  this.supabase = supabase;
@@ -86,4 +82,26 @@ export class DbModule {
86
82
  });
87
83
  });
88
84
  }
85
+ /**
86
+ * Search a plugin table using vector similarity (cosine distance).
87
+ * The table must have a vector column named 'embedding' defined in db.config.ts.
88
+ * @param params Search parameters
89
+ * @returns Matching rows sorted by similarity
90
+ */
91
+ vectorSearch(params) {
92
+ return __awaiter(this, void 0, void 0, function* () {
93
+ const response = yield fetch(`${this.rimoriInfo.backendUrl}/plugin-search/vector-search`, {
94
+ method: 'POST',
95
+ headers: {
96
+ 'Content-Type': 'application/json',
97
+ Authorization: `Bearer ${this.rimoriInfo.token}`,
98
+ },
99
+ body: JSON.stringify(params),
100
+ });
101
+ if (!response.ok) {
102
+ throw new Error(`Vector search failed: ${response.statusText}`);
103
+ }
104
+ return yield response.json();
105
+ });
106
+ }
89
107
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rimori/client",
3
- "version": "2.5.26-next.1",
3
+ "version": "2.5.26-next.3",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "repository": {
@@ -31,7 +31,7 @@
31
31
  },
32
32
  "scripts": {
33
33
  "build": "tsc",
34
- "dev": "tsc -w --preserveWatchOutput",
34
+ "dev": "git pull && tsc -w --preserveWatchOutput",
35
35
  "lint": "eslint . --fix",
36
36
  "format": "prettier --write ."
37
37
  },