Initial commit

This commit is contained in:
2026-07-10 08:31:27 +08:00
commit 2b74c30d36
134 changed files with 21801 additions and 0 deletions
+146
View File
@@ -0,0 +1,146 @@
import { getApiProfile, profileToLlmConfig } from "../config/api-profiles.js";
import {
OpenAiCompatibleProvider,
type LlmProvider,
} from "../llm/client.js";
import { PresetLlmProvider } from "../llm/preset-wrapper.js";
import { resolveActivePreset } from "../preset/store.js";
import { loadAppSettings } from "../config/settings.js";
import {
TokenTrackingProvider,
type LlmTrackingContext,
} from "../llm/token-tracker.js";
import type { ParsedWorkerSkill, SkillWorkerLlmBindings } from "./types.js";
type LlmTrackingRef = { current: LlmTrackingContext };
/** 按 ApiProfile.id 构建 LLM;找不到 profile 时回退 fallback */
export function createLlmForProfileId(
profileId: string,
fallback: LlmProvider,
trackingRef?: LlmTrackingRef,
): LlmProvider {
const profile = getApiProfile(profileId);
if (!profile?.apiKey?.trim()) {
return fallback;
}
let inner: LlmProvider = new OpenAiCompatibleProvider(
profileToLlmConfig(profile),
);
const preset = resolveActivePreset(loadAppSettings().activePresetId);
if (preset) {
inner = new PresetLlmProvider(inner, () =>
resolveActivePreset(loadAppSettings().activePresetId),
);
}
if (!trackingRef) return inner;
return new TokenTrackingProvider(inner, () => trackingRef.current);
}
/**
* 解析 worker 应使用的 LLM。
*
* 优先级:
* 1. worker SKILL frontmatter `llmProfileId`
* 2. llm-bindings.yaml `workers[id].byRole[roleId]`(roleId 来自 slots.世界.当前角色.id)
* 3. llm-bindings.yaml `workers[id].profileId`
* 4. fallback(会话默认 profile,与总管相同)
*/
export function resolveWorkerLlmProvider(options: {
worker: ParsedWorkerSkill;
bindings?: SkillWorkerLlmBindings;
slots: Record<string, unknown>;
fallbackLlm: LlmProvider;
trackingRef?: LlmTrackingRef;
}): LlmProvider {
const { worker, bindings, slots, fallbackLlm, trackingRef } = options;
if (worker.llmProfileId?.trim()) {
return createLlmForProfileId(
worker.llmProfileId.trim(),
fallbackLlm,
trackingRef,
);
}
const workerBinding = bindings?.workers?.[worker.id];
if (workerBinding?.byRole) {
const roleId = slotString(slots, "世界.当前角色.id");
if (roleId && workerBinding.byRole[roleId]?.trim()) {
return createLlmForProfileId(
workerBinding.byRole[roleId].trim(),
fallbackLlm,
trackingRef,
);
}
}
if (workerBinding?.profileId?.trim()) {
return createLlmForProfileId(
workerBinding.profileId.trim(),
fallbackLlm,
trackingRef,
);
}
if (bindings?.defaultProfileId?.trim()) {
return createLlmForProfileId(
bindings.defaultProfileId.trim(),
fallbackLlm,
trackingRef,
);
}
return fallbackLlm;
}
function slotString(slots: Record<string, unknown>, key: string): string | undefined {
const val = slots[key];
return typeof val === "string" && val.trim() ? val.trim() : undefined;
}
/** 角色 tag:仅用户可见 */
export const ROLE_USER_ONLY_SUFFIXES = [".思考", ".推理.候选"] as const;
/** 角色 tag:对其余角色 agent 可见(经 world-engine 公开) */
export const ROLE_AGENT_VISIBLE_SUFFIXES = [".行动", ".行动.候选"] as const;
export function isRoleUserOnlyTag(tag: string): boolean {
return ROLE_USER_ONLY_SUFFIXES.some((s) => tag.endsWith(s));
}
export function isRoleAgentVisibleTag(tag: string): boolean {
return ROLE_AGENT_VISIBLE_SUFFIXES.some((s) => tag.endsWith(s));
}
/**
* 供 executor:角色 worker 只能看到当前角色的私有 tag。
* 其他角色的思考永不可见;对方行动仅经 world-engine 分发后的可见信息/公开叙述获知。
*/
export function filterInputsForRolePerspective(
inputs: Record<string, string>,
slots: Record<string, unknown>,
): Record<string, string> {
const roleId = slotString(slots, "世界.当前角色.id");
if (!roleId) return inputs;
const filtered: Record<string, string> = {};
for (const [tag, content] of Object.entries(inputs)) {
const rolePrefix = `角色.${roleId}.`;
const otherRoleMatch = tag.match(/^角色\.([^.]+)\./);
if (otherRoleMatch && otherRoleMatch[1] !== roleId) {
continue;
}
if (tag.startsWith("角色.") && !tag.startsWith(rolePrefix)) {
continue;
}
if (tag.startsWith(rolePrefix) && isRoleUserOnlyTag(tag)) {
continue;
}
filtered[tag] = content;
}
return filtered;
}
export type { LlmTrackingContext };