Studio: prompt variables into prompt editor (#6434)
* add custom and system variable feature in system prompt * missing function use * feat: prompt variables editor ux Co-authored-by: CodeMan62 <175127021+CodeMan62@users.noreply.github.com> * fix: guard prompt variable defaults * refine prompt variables editor layout * fix: harden prompt variable substitution * [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci * Refine prompt variables editor copy and built-in token labels --------- Co-authored-by: CodeMan62 <sharmahimanshu15082007@gmail.com> Co-authored-by: CodeMan62 <175127021+CodeMan62@users.noreply.github.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: Leo Borcherding <borchborchmail@gmail.com>
This commit is contained in:
parent
a636693019
commit
c873ef052d
8 changed files with 322 additions and 16 deletions
|
|
@ -185,6 +185,124 @@ function wait(ms: number): Promise<void> {
|
|||
return new Promise((resolve) => setTimeout(resolve, ms));
|
||||
}
|
||||
|
||||
function parseSystemVariablesMap(raw: string): Record<string, unknown> {
|
||||
if (!raw.trim()) {
|
||||
return {};
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(raw) as unknown;
|
||||
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) {
|
||||
return parsed as Record<string, unknown>;
|
||||
}
|
||||
} catch {
|
||||
// Invalid JSON: keep unresolved placeholders in output prompt.
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
function hasOwn(object: object, key: string): boolean {
|
||||
return Object.prototype.hasOwnProperty.call(object, key);
|
||||
}
|
||||
|
||||
function getNestedValue(
|
||||
values: Record<string, unknown>,
|
||||
path: string,
|
||||
): unknown | undefined {
|
||||
const parts = path.split(".").map((part) => part.trim()).filter(Boolean);
|
||||
if (parts.length === 0) {
|
||||
return undefined;
|
||||
}
|
||||
let current: unknown = values;
|
||||
for (const part of parts) {
|
||||
if (!current || typeof current !== "object" || Array.isArray(current)) {
|
||||
return undefined;
|
||||
}
|
||||
if (!hasOwn(current, part)) {
|
||||
return undefined;
|
||||
}
|
||||
current = (current as Record<string, unknown>)[part];
|
||||
}
|
||||
return current;
|
||||
}
|
||||
|
||||
function padDatePart(value: number): string {
|
||||
return String(value).padStart(2, "0");
|
||||
}
|
||||
|
||||
function formatLocalDate(now: Date): string {
|
||||
return [
|
||||
now.getFullYear(),
|
||||
padDatePart(now.getMonth() + 1),
|
||||
padDatePart(now.getDate()),
|
||||
].join("-");
|
||||
}
|
||||
|
||||
function formatLocalTime(now: Date): string {
|
||||
return [
|
||||
padDatePart(now.getHours()),
|
||||
padDatePart(now.getMinutes()),
|
||||
padDatePart(now.getSeconds()),
|
||||
].join(":");
|
||||
}
|
||||
|
||||
function formatTimezoneOffset(now: Date): string {
|
||||
const offsetMinutes = -now.getTimezoneOffset();
|
||||
const sign = offsetMinutes >= 0 ? "+" : "-";
|
||||
const abs = Math.abs(offsetMinutes);
|
||||
const hours = Math.floor(abs / 60);
|
||||
const minutes = abs % 60;
|
||||
return `${sign}${padDatePart(hours)}:${padDatePart(minutes)}`;
|
||||
}
|
||||
|
||||
function stringifyTemplateValue(value: unknown): string {
|
||||
if (value == null) {
|
||||
return "";
|
||||
}
|
||||
if (typeof value === "string") {
|
||||
return value;
|
||||
}
|
||||
if (typeof value === "number" || typeof value === "boolean") {
|
||||
return String(value);
|
||||
}
|
||||
try {
|
||||
return JSON.stringify(value);
|
||||
} catch {
|
||||
return String(value);
|
||||
}
|
||||
}
|
||||
|
||||
function resolveSystemPromptVariables(
|
||||
prompt: string,
|
||||
customVariablesRaw: string,
|
||||
): string {
|
||||
if (!prompt) {
|
||||
return prompt;
|
||||
}
|
||||
const now = new Date();
|
||||
const localDate = formatLocalDate(now);
|
||||
const localTime = formatLocalTime(now);
|
||||
const systemVariables: Record<string, string> = {
|
||||
$date: localDate,
|
||||
$time: localTime,
|
||||
$now: `${localDate}T${localTime}${formatTimezoneOffset(now)}`,
|
||||
};
|
||||
const customVariables = parseSystemVariablesMap(customVariablesRaw);
|
||||
return prompt.replaceAll(
|
||||
/{{\s*([a-zA-Z_$][a-zA-Z0-9_$.-]*)\s*}}/g,
|
||||
(full, keyRaw) => {
|
||||
const key = String(keyRaw).trim();
|
||||
if (hasOwn(systemVariables, key)) {
|
||||
return systemVariables[key] ?? full;
|
||||
}
|
||||
const resolved = getNestedValue(customVariables, key);
|
||||
if (resolved === undefined) {
|
||||
return full;
|
||||
}
|
||||
return stringifyTemplateValue(resolved);
|
||||
},
|
||||
);
|
||||
}
|
||||
|
||||
export const ThreadAutosaveHandle: ThreadAutosaveHandle = {
|
||||
registerFirstSave(threadId, promise) {
|
||||
const trackedPromise = promise.catch(() => {});
|
||||
|
|
@ -1778,7 +1896,14 @@ export function createOpenAIStreamAdapter(): ChatModelAdapter {
|
|||
}
|
||||
|
||||
const safeSystemPrompt =
|
||||
typeof params.systemPrompt === "string" ? params.systemPrompt : "";
|
||||
typeof params.systemPrompt === "string"
|
||||
? resolveSystemPromptVariables(
|
||||
params.systemPrompt,
|
||||
typeof params.systemVariables === "string"
|
||||
? params.systemVariables
|
||||
: "",
|
||||
)
|
||||
: "";
|
||||
const projectInstructions =
|
||||
await resolveProjectInstructions(resolvedThreadId);
|
||||
const combinedSystemPrompt = [
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue