u
This commit is contained in:
@@ -109,9 +109,9 @@ async function buildSystemPrompt(vfsRoot, googleAuthId) {
|
|||||||
`Date: ${today}`,
|
`Date: ${today}`,
|
||||||
'',
|
'',
|
||||||
'YOUR TOOLS:',
|
'YOUR TOOLS:',
|
||||||
'1. dashback_request: HTTP requests (GET, POST, PUT, PATCH, DELETE) against Dashback endpoints.',
|
'1. dashback_request: HTTP requests (GET, POST, PUT, PATCH, DELETE) against QuixPOS Article Management System.',
|
||||||
'2. ask_user: Prompt user for clarification or decisions. Pauses execution until response.',
|
'2. ask_user: Prompt user for clarification or decisions. Pauses execution until response.',
|
||||||
'3. javascript_execute: Sandboxed Node environment for computation, aggregation, and binary I/O. Assign module.exports. API: fetch("/path"). VFS: fs/vfs.',
|
'3. javascript_execute: Sandboxed Node environment for computation, aggregation, and binary I/O. Assign module.exports. API: fetch("/path"). vfs.',
|
||||||
'4. find_files: List VFS files. Set skills=true for SKILL files.',
|
'4. find_files: List VFS files. Set skills=true for SKILL files.',
|
||||||
'5. read_file: Read VFS text file (seek/limit windowed).',
|
'5. read_file: Read VFS text file (seek/limit windowed).',
|
||||||
'6. patch: Create, overwrite, or replace text in VFS files (only text write path; creates SKILL files).',
|
'6. patch: Create, overwrite, or replace text in VFS files (only text write path; creates SKILL files).',
|
||||||
@@ -126,13 +126,12 @@ async function buildSystemPrompt(vfsRoot, googleAuthId) {
|
|||||||
'',
|
'',
|
||||||
'VIRTUAL FILESYSTEM:',
|
'VIRTUAL FILESYSTEM:',
|
||||||
'- Persistent per-company VFS. Paths are root-relative forward-slash (e.g. "labels/sheet1.png").',
|
'- Persistent per-company VFS. Paths are root-relative forward-slash (e.g. "labels/sheet1.png").',
|
||||||
'- Text: list with find_files, read with read_file, write with patch. Binary/buffers: javascript_execute.',
|
'- Text: list with find_files, read with read_file, write with patch.',
|
||||||
'- Never invent paths; verify with find_files before reading or referencing.',
|
|
||||||
'',
|
'',
|
||||||
'SKILL FILES:',
|
'SKILL FILES:',
|
||||||
'- Reusable instruction docs: "<slug>.SKILL" at VFS root. First line is title, followed by markdown.',
|
'- Reusable instruction docs: "<slug>.SKILL" at VFS root. First line is title, followed by markdown.',
|
||||||
'- Root-only (no "/"). Discover via find_files({ skills: true }).',
|
'- Root-only (no "/"). Discover via find_files({ skills: true }).',
|
||||||
'- Read with read_file BEFORE starting covered tasks; follow instructions. Propose saving improved procedures as new SKILL files.',
|
'- Read with read_file BEFORE starting covered tasks. Propose saving improved procedures as new SKILL files.',
|
||||||
'- Exempt from reset_vfs. Delete permanently via rm.',
|
'- Exempt from reset_vfs. Delete permanently via rm.',
|
||||||
'',
|
'',
|
||||||
buildSkillIndex(vfsRoot),
|
buildSkillIndex(vfsRoot),
|
||||||
@@ -143,10 +142,7 @@ async function buildSystemPrompt(vfsRoot, googleAuthId) {
|
|||||||
'- `image/` is READ-ONLY database storage. Write generated images elsewhere (e.g. "images/…").',
|
'- `image/` is READ-ONLY database storage. Write generated images elsewhere (e.g. "images/…").',
|
||||||
'',
|
'',
|
||||||
'OPERATING GUIDELINES:',
|
'OPERATING GUIDELINES:',
|
||||||
'- Endpoint reference: see DASHBACK API ROUTES below.',
|
|
||||||
'- SELLING PRICES: In article responses (GET /article/{sku}), selling price field is `net` (NOT `price`). Schema in `article.prices`: { minmultiplier, net, taxRate, customergroupId }.',
|
|
||||||
'- GROSS vs NET PRICES: User prices are almost always GROSS (incl. VAT); API requires NET. Formula: `net = gross / (1 + taxRate / 100)`. Compute in javascript_execute with high precision (4–6 decimals) to prevent rounding error.',
|
'- GROSS vs NET PRICES: User prices are almost always GROSS (incl. VAT); API requires NET. Formula: `net = gross / (1 + taxRate / 100)`. Compute in javascript_execute with high precision (4–6 decimals) to prevent rounding error.',
|
||||||
'- WIRE TYPES: Strict validation (IDs, weight, price, quantity must be numbers; flags booleans).',
|
|
||||||
'- DISPLAYING GENERATED IMAGES: Inline markdown images supported. Embed written VFS image paths: ``. Other files (PDF, JSON, CSV) do not render inline; provide download links.',
|
'- DISPLAYING GENERATED IMAGES: Inline markdown images supported. Embed written VFS image paths: ``. Other files (PDF, JSON, CSV) do not render inline; provide download links.',
|
||||||
'- LABEL TEMPLATES (/labeltemplate): Must use Dashback JSON schema `{ "objects": [ ... ] }`. Do NOT use ZPL.',
|
'- LABEL TEMPLATES (/labeltemplate): Must use Dashback JSON schema `{ "objects": [ ... ] }`. Do NOT use ZPL.',
|
||||||
'- Format output with markdown tables, bullet points, or bold key-value pairs.',
|
'- Format output with markdown tables, bullet points, or bold key-value pairs.',
|
||||||
@@ -168,6 +164,38 @@ async function buildSystemPrompt(vfsRoot, googleAuthId) {
|
|||||||
* `idle`/`busy` state. The finer SSE states (`thinking`, `working`, `done`)
|
* `idle`/`busy` state. The finer SSE states (`thinking`, `working`, `done`)
|
||||||
* and the streamed `delta` chunks are SSE-only.
|
* and the streamed `delta` chunks are SSE-only.
|
||||||
*/
|
*/
|
||||||
|
function parseUtcDate(val) {
|
||||||
|
if (!val) return new Date();
|
||||||
|
if (val instanceof Date) return isNaN(val.getTime()) ? new Date() : val;
|
||||||
|
if (typeof val === 'string') {
|
||||||
|
const s = val.trim();
|
||||||
|
if (/^\d{4}-\d{2}-\d{2}[ T]\d{2}:\d{2}:\d{2}/.test(s) && !s.endsWith('Z') && !/[+-]\d{2}(:?\d{2})?$/.test(s)) {
|
||||||
|
const parsed = new Date(s.replace(' ', 'T') + 'Z');
|
||||||
|
if (!isNaN(parsed.getTime())) return parsed;
|
||||||
|
}
|
||||||
|
const parsed = new Date(s);
|
||||||
|
if (!isNaN(parsed.getTime())) return parsed;
|
||||||
|
}
|
||||||
|
return new Date();
|
||||||
|
}
|
||||||
|
|
||||||
|
export function formatTimePrefix(createdDate) {
|
||||||
|
const d = parseUtcDate(createdDate);
|
||||||
|
const parts = new Intl.DateTimeFormat('en-GB', {
|
||||||
|
timeZone: 'Europe/Berlin',
|
||||||
|
hour: '2-digit',
|
||||||
|
minute: '2-digit',
|
||||||
|
hour12: false,
|
||||||
|
timeZoneName: 'short',
|
||||||
|
}).formatToParts(d);
|
||||||
|
const hour = parts.find((p) => p.type === 'hour')?.value.padStart(2, '0') || '00';
|
||||||
|
const minute = parts.find((p) => p.type === 'minute')?.value.padStart(2, '0') || '00';
|
||||||
|
let tz = parts.find((p) => p.type === 'timeZoneName')?.value || 'MEST';
|
||||||
|
if (tz === 'CEST') tz = 'MEST';
|
||||||
|
else if (tz === 'CET') tz = 'MET';
|
||||||
|
return `[${hour}:${minute}${tz}] `;
|
||||||
|
}
|
||||||
|
|
||||||
export function historyToLlmMessages(history) {
|
export function historyToLlmMessages(history) {
|
||||||
const messages = [];
|
const messages = [];
|
||||||
for (const m of history) {
|
for (const m of history) {
|
||||||
@@ -179,7 +207,12 @@ export function historyToLlmMessages(history) {
|
|||||||
payload = { content: m.message };
|
payload = { content: m.message };
|
||||||
}
|
}
|
||||||
if (m.role === 'user') {
|
if (m.role === 'user') {
|
||||||
messages.push({ role: 'user', content: payload.content ?? (typeof m.message === 'string' ? m.message : '') });
|
const rawContent = payload.content ?? (typeof m.message === 'string' ? m.message : '');
|
||||||
|
const timePrefix = formatTimePrefix(m.created_at);
|
||||||
|
const content = /^\s*\[\d{2}:\d{2}[A-Za-z]+\]\s*/.test(rawContent)
|
||||||
|
? rawContent
|
||||||
|
: `${timePrefix}${rawContent}`;
|
||||||
|
messages.push({ role: 'user', content });
|
||||||
} else if (m.role === 'assistant') {
|
} else if (m.role === 'assistant') {
|
||||||
messages.push({
|
messages.push({
|
||||||
role: 'assistant',
|
role: 'assistant',
|
||||||
|
|||||||
Reference in New Issue
Block a user