u
This commit is contained in:
@@ -6,7 +6,8 @@ Upsert company setting
|
|||||||
|
|
||||||
Sets (upserts) a company setting for the selected company.
|
Sets (upserts) a company setting for the selected company.
|
||||||
Only a fixed allow-list of keys can be set: `ui.theme`, `keys.tavily`,
|
Only a fixed allow-list of keys can be set: `ui.theme`, `keys.tavily`,
|
||||||
`keys.openrouter`, `keys.serper`, `labelprinter.addr`, `labelprinter.type`.
|
`keys.openrouter`, `keys.serper`, `systemprompt-prefix`,
|
||||||
|
`systemprompt-suffix`, `default-model`.
|
||||||
|
|
||||||
|
|
||||||
## Path parameters
|
## Path parameters
|
||||||
|
|||||||
@@ -5,8 +5,9 @@ const ALLOWED_KEYS = [
|
|||||||
'keys.tavily',
|
'keys.tavily',
|
||||||
'keys.openrouter',
|
'keys.openrouter',
|
||||||
'keys.serper',
|
'keys.serper',
|
||||||
'labelprinter.addr',
|
'systemprompt-prefix',
|
||||||
'labelprinter.type',
|
'systemprompt-suffix',
|
||||||
|
'default-model',
|
||||||
];
|
];
|
||||||
|
|
||||||
function requireSelectedCompany(req) {
|
function requireSelectedCompany(req) {
|
||||||
@@ -206,7 +207,8 @@ export function register(app, basePath = '') {
|
|||||||
* description: |
|
* description: |
|
||||||
* Sets (upserts) a company setting for the selected company.
|
* Sets (upserts) a company setting for the selected company.
|
||||||
* Only a fixed allow-list of keys can be set: `ui.theme`, `keys.tavily`,
|
* Only a fixed allow-list of keys can be set: `ui.theme`, `keys.tavily`,
|
||||||
* `keys.openrouter`, `keys.serper`, `labelprinter.addr`, `labelprinter.type`.
|
* `keys.openrouter`, `keys.serper`, `systemprompt-prefix`,
|
||||||
|
* `systemprompt-suffix`, `default-model`.
|
||||||
* security:
|
* security:
|
||||||
* - cookieAuth: []
|
* - cookieAuth: []
|
||||||
* x-markdown: companysetting/set.md
|
* x-markdown: companysetting/set.md
|
||||||
|
|||||||
@@ -73,9 +73,27 @@ function buildSkillIndex(vfsRoot) {
|
|||||||
* (docs/api/routes.md), read on each run so it stays current.
|
* (docs/api/routes.md), read on each run so it stays current.
|
||||||
*
|
*
|
||||||
* @param {string} vfsRoot Resolved VFS root for this user's company, used to list SKILL files.
|
* @param {string} vfsRoot Resolved VFS root for this user's company, used to list SKILL files.
|
||||||
* @returns {string}
|
* @param {number} [googleAuthId]
|
||||||
|
* @returns {Promise<string>}
|
||||||
*/
|
*/
|
||||||
async function buildSystemPrompt(vfsRoot) {
|
async function buildSystemPrompt(vfsRoot, googleAuthId) {
|
||||||
|
let prefix = '';
|
||||||
|
let suffix = '';
|
||||||
|
if (googleAuthId) {
|
||||||
|
try {
|
||||||
|
const prefixSetting = await dbCompanySetting.getByKey(googleAuthId, 'systemprompt-prefix', { maskWriteonly: false });
|
||||||
|
prefix = prefixSetting?.value?.trim() || '';
|
||||||
|
} catch (err) {
|
||||||
|
console.error('conversation: failed to read systemprompt-prefix:', err.message);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
const suffixSetting = await dbCompanySetting.getByKey(googleAuthId, 'systemprompt-suffix', { maskWriteonly: false });
|
||||||
|
suffix = suffixSetting?.value?.trim() || '';
|
||||||
|
} catch (err) {
|
||||||
|
console.error('conversation: failed to read systemprompt-suffix:', err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
let routesDoc = '';
|
let routesDoc = '';
|
||||||
try {
|
try {
|
||||||
routesDoc = readFileSync(ROUTES_DOC_PATH, 'utf8').trim();
|
routesDoc = readFileSync(ROUTES_DOC_PATH, 'utf8').trim();
|
||||||
@@ -83,7 +101,7 @@ async function buildSystemPrompt(vfsRoot) {
|
|||||||
console.error('conversation: failed to read routes doc:', err);
|
console.error('conversation: failed to read routes doc:', err);
|
||||||
}
|
}
|
||||||
const today = new Date().toISOString().split('T')[0];
|
const today = new Date().toISOString().split('T')[0];
|
||||||
return [
|
const basePrompt = [
|
||||||
'You are an intelligent assistant specialized in exploring, querying, and managing data from the Dashback REST API.',
|
'You are an intelligent assistant specialized in exploring, querying, and managing data from the Dashback REST API.',
|
||||||
`Today's date: ${today}`,
|
`Today's date: ${today}`,
|
||||||
'',
|
'',
|
||||||
@@ -129,6 +147,12 @@ async function buildSystemPrompt(vfsRoot) {
|
|||||||
'- Format responses clearly using markdown tables, bullet lists, or bold key-value pairs where appropriate.',
|
'- Format responses clearly using markdown tables, bullet lists, or bold key-value pairs where appropriate.',
|
||||||
...(routesDoc ? ['', 'DASHBACK API ROUTES:', routesDoc] : []),
|
...(routesDoc ? ['', 'DASHBACK API ROUTES:', routesDoc] : []),
|
||||||
].join('\n');
|
].join('\n');
|
||||||
|
|
||||||
|
const sections = [];
|
||||||
|
if (prefix) sections.push(prefix);
|
||||||
|
sections.push(basePrompt);
|
||||||
|
if (suffix) sections.push(suffix);
|
||||||
|
return sections.join('\n\n');
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -351,7 +375,7 @@ export class ConversationManager {
|
|||||||
const defaultModel = await resolveDefaultModel(googleAuthId);
|
const defaultModel = await resolveDefaultModel(googleAuthId);
|
||||||
const hasCustomKey = await hasConfiguredOpenRouterKey(googleAuthId);
|
const hasCustomKey = await hasConfiguredOpenRouterKey(googleAuthId);
|
||||||
const storedModel = typeof conversation.model === 'string' ? conversation.model.trim() : '';
|
const storedModel = typeof conversation.model === 'string' ? conversation.model.trim() : '';
|
||||||
const model = (!storedModel || RETIRED_MODELS.has(storedModel) || (storedModel === env.openrouterFreeModel && hasCustomKey))
|
const model = (!storedModel || RETIRED_MODELS.has(storedModel) || (storedModel === env.openrouterFreeModel && (hasCustomKey || defaultModel !== env.openrouterFreeModel)))
|
||||||
? defaultModel
|
? defaultModel
|
||||||
: storedModel;
|
: storedModel;
|
||||||
const apiKey = await resolveOpenRouterKey(googleAuthId);
|
const apiKey = await resolveOpenRouterKey(googleAuthId);
|
||||||
@@ -369,7 +393,7 @@ export class ConversationManager {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const messages = [
|
const messages = [
|
||||||
{ role: 'system', content: await buildSystemPrompt(vfsRoot) },
|
{ role: 'system', content: await buildSystemPrompt(vfsRoot, googleAuthId) },
|
||||||
...history.map(toLlmMessage),
|
...history.map(toLlmMessage),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|||||||
@@ -47,13 +47,27 @@ export async function hasConfiguredOpenRouterKey(googleAuthId) {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Resolve the default model for a user/company:
|
* Resolve the default model for a user/company:
|
||||||
* - As soon as the user/company has configured `keys.openrouter`, use `env.openrouterPaidModel`.
|
* - If companysetting `default-model` is set, return it (works in both paid and no-key modes).
|
||||||
* - Otherwise (using the server's free key), use `env.openrouterFreeModel`.
|
* - Otherwise, if the user/company has configured `keys.openrouter`, use `env.openrouterPaidModel`
|
||||||
|
* (which defaults to 'openai/gpt-6-luna:floor').
|
||||||
|
* - Otherwise (no key set / free mode), use `env.openrouterFreeModel`
|
||||||
|
* (which defaults to 'openrouter/free').
|
||||||
*
|
*
|
||||||
* @param {number} [googleAuthId]
|
* @param {number} [googleAuthId]
|
||||||
* @returns {Promise<string>}
|
* @returns {Promise<string>}
|
||||||
*/
|
*/
|
||||||
export async function resolveDefaultModel(googleAuthId) {
|
export async function resolveDefaultModel(googleAuthId) {
|
||||||
|
if (googleAuthId) {
|
||||||
|
try {
|
||||||
|
const setting = await dbCompanySetting.getByKey(googleAuthId, 'default-model', { maskWriteonly: false });
|
||||||
|
const customModel = setting?.value?.trim();
|
||||||
|
if (customModel) return customModel;
|
||||||
|
} catch (err) {
|
||||||
|
console.error('default-model: failed to read company setting:', err.message);
|
||||||
|
}
|
||||||
|
}
|
||||||
const hasKey = await hasConfiguredOpenRouterKey(googleAuthId);
|
const hasKey = await hasConfiguredOpenRouterKey(googleAuthId);
|
||||||
return hasKey ? env.openrouterPaidModel : env.openrouterFreeModel;
|
return hasKey
|
||||||
|
? (env.openrouterPaidModel || 'openai/gpt-6-luna:floor')
|
||||||
|
: (env.openrouterFreeModel || 'openrouter/free');
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user