mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-09 10:07:41 +00:00
chore: Enable "curly" rule to avoid single-statement if confusion/errors.
This commit is contained in:
@@ -89,10 +89,16 @@ const binaryCache = new Map<string, Promise<string | null>>();
|
||||
const geminiProbeCache = new Map<string, Promise<boolean>>();
|
||||
|
||||
function expandHomeDir(value: string): string {
|
||||
if (!value.startsWith("~")) return value;
|
||||
if (!value.startsWith("~")) {
|
||||
return value;
|
||||
}
|
||||
const home = os.homedir();
|
||||
if (value === "~") return home;
|
||||
if (value.startsWith("~/")) return path.join(home, value.slice(2));
|
||||
if (value === "~") {
|
||||
return home;
|
||||
}
|
||||
if (value.startsWith("~/")) {
|
||||
return path.join(home, value.slice(2));
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
@@ -101,9 +107,13 @@ function hasPathSeparator(value: string): boolean {
|
||||
}
|
||||
|
||||
function candidateBinaryNames(name: string): string[] {
|
||||
if (process.platform !== "win32") return [name];
|
||||
if (process.platform !== "win32") {
|
||||
return [name];
|
||||
}
|
||||
const ext = path.extname(name);
|
||||
if (ext) return [name];
|
||||
if (ext) {
|
||||
return [name];
|
||||
}
|
||||
const pathext = (process.env.PATHEXT ?? ".EXE;.CMD;.BAT;.COM")
|
||||
.split(";")
|
||||
.map((item) => item.trim())
|
||||
@@ -116,8 +126,12 @@ function candidateBinaryNames(name: string): string[] {
|
||||
async function isExecutable(filePath: string): Promise<boolean> {
|
||||
try {
|
||||
const stat = await fs.stat(filePath);
|
||||
if (!stat.isFile()) return false;
|
||||
if (process.platform === "win32") return true;
|
||||
if (!stat.isFile()) {
|
||||
return false;
|
||||
}
|
||||
if (process.platform === "win32") {
|
||||
return true;
|
||||
}
|
||||
await fs.access(filePath, fsConstants.X_OK);
|
||||
return true;
|
||||
} catch {
|
||||
@@ -127,25 +141,35 @@ async function isExecutable(filePath: string): Promise<boolean> {
|
||||
|
||||
async function findBinary(name: string): Promise<string | null> {
|
||||
const cached = binaryCache.get(name);
|
||||
if (cached) return cached;
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
const resolved = (async () => {
|
||||
const direct = expandHomeDir(name.trim());
|
||||
if (direct && hasPathSeparator(direct)) {
|
||||
for (const candidate of candidateBinaryNames(direct)) {
|
||||
if (await isExecutable(candidate)) return candidate;
|
||||
if (await isExecutable(candidate)) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const searchName = name.trim();
|
||||
if (!searchName) return null;
|
||||
if (!searchName) {
|
||||
return null;
|
||||
}
|
||||
const pathEntries = (process.env.PATH ?? "").split(path.delimiter);
|
||||
const candidates = candidateBinaryNames(searchName);
|
||||
for (const entryRaw of pathEntries) {
|
||||
const entry = expandHomeDir(entryRaw.trim().replace(/^"(.*)"$/, "$1"));
|
||||
if (!entry) continue;
|
||||
if (!entry) {
|
||||
continue;
|
||||
}
|
||||
for (const candidate of candidates) {
|
||||
const fullPath = path.join(entry, candidate);
|
||||
if (await isExecutable(fullPath)) return fullPath;
|
||||
if (await isExecutable(fullPath)) {
|
||||
return fullPath;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -160,7 +184,9 @@ async function hasBinary(name: string): Promise<boolean> {
|
||||
}
|
||||
|
||||
async function fileExists(filePath?: string | null): Promise<boolean> {
|
||||
if (!filePath) return false;
|
||||
if (!filePath) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
await fs.stat(filePath);
|
||||
return true;
|
||||
@@ -172,7 +198,9 @@ async function fileExists(filePath?: string | null): Promise<boolean> {
|
||||
function extractLastJsonObject(raw: string): unknown {
|
||||
const trimmed = raw.trim();
|
||||
const start = trimmed.lastIndexOf("{");
|
||||
if (start === -1) return null;
|
||||
if (start === -1) {
|
||||
return null;
|
||||
}
|
||||
const slice = trimmed.slice(start);
|
||||
try {
|
||||
return JSON.parse(slice);
|
||||
@@ -183,9 +211,13 @@ function extractLastJsonObject(raw: string): unknown {
|
||||
|
||||
function extractGeminiResponse(raw: string): string | null {
|
||||
const payload = extractLastJsonObject(raw);
|
||||
if (!payload || typeof payload !== "object") return null;
|
||||
if (!payload || typeof payload !== "object") {
|
||||
return null;
|
||||
}
|
||||
const response = (payload as { response?: unknown }).response;
|
||||
if (typeof response !== "string") return null;
|
||||
if (typeof response !== "string") {
|
||||
return null;
|
||||
}
|
||||
const trimmed = response.trim();
|
||||
return trimmed || null;
|
||||
}
|
||||
@@ -193,9 +225,13 @@ function extractGeminiResponse(raw: string): string | null {
|
||||
function extractSherpaOnnxText(raw: string): string | null {
|
||||
const tryParse = (value: string): string | null => {
|
||||
const trimmed = value.trim();
|
||||
if (!trimmed) return null;
|
||||
if (!trimmed) {
|
||||
return null;
|
||||
}
|
||||
const head = trimmed[0];
|
||||
if (head !== "{" && head !== '"') return null;
|
||||
if (head !== "{" && head !== '"') {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
const parsed = JSON.parse(trimmed) as unknown;
|
||||
if (typeof parsed === "string") {
|
||||
@@ -212,7 +248,9 @@ function extractSherpaOnnxText(raw: string): string | null {
|
||||
};
|
||||
|
||||
const direct = tryParse(raw);
|
||||
if (direct) return direct;
|
||||
if (direct) {
|
||||
return direct;
|
||||
}
|
||||
|
||||
const lines = raw
|
||||
.split("\n")
|
||||
@@ -220,16 +258,22 @@ function extractSherpaOnnxText(raw: string): string | null {
|
||||
.filter(Boolean);
|
||||
for (let i = lines.length - 1; i >= 0; i -= 1) {
|
||||
const parsed = tryParse(lines[i] ?? "");
|
||||
if (parsed) return parsed;
|
||||
if (parsed) {
|
||||
return parsed;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async function probeGeminiCli(): Promise<boolean> {
|
||||
const cached = geminiProbeCache.get("gemini");
|
||||
if (cached) return cached;
|
||||
if (cached) {
|
||||
return cached;
|
||||
}
|
||||
const resolved = (async () => {
|
||||
if (!(await hasBinary("gemini"))) return false;
|
||||
if (!(await hasBinary("gemini"))) {
|
||||
return false;
|
||||
}
|
||||
try {
|
||||
const { stdout } = await runExec("gemini", ["--output-format", "json", "ok"], {
|
||||
timeoutMs: 8000,
|
||||
@@ -244,11 +288,15 @@ async function probeGeminiCli(): Promise<boolean> {
|
||||
}
|
||||
|
||||
async function resolveLocalWhisperCppEntry(): Promise<MediaUnderstandingModelConfig | null> {
|
||||
if (!(await hasBinary("whisper-cli"))) return null;
|
||||
if (!(await hasBinary("whisper-cli"))) {
|
||||
return null;
|
||||
}
|
||||
const envModel = process.env.WHISPER_CPP_MODEL?.trim();
|
||||
const defaultModel = "/opt/homebrew/share/whisper-cpp/for-tests-ggml-tiny.bin";
|
||||
const modelPath = envModel && (await fileExists(envModel)) ? envModel : defaultModel;
|
||||
if (!(await fileExists(modelPath))) return null;
|
||||
if (!(await fileExists(modelPath))) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
type: "cli",
|
||||
command: "whisper-cli",
|
||||
@@ -257,7 +305,9 @@ async function resolveLocalWhisperCppEntry(): Promise<MediaUnderstandingModelCon
|
||||
}
|
||||
|
||||
async function resolveLocalWhisperEntry(): Promise<MediaUnderstandingModelConfig | null> {
|
||||
if (!(await hasBinary("whisper"))) return null;
|
||||
if (!(await hasBinary("whisper"))) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
type: "cli",
|
||||
command: "whisper",
|
||||
@@ -276,17 +326,29 @@ async function resolveLocalWhisperEntry(): Promise<MediaUnderstandingModelConfig
|
||||
}
|
||||
|
||||
async function resolveSherpaOnnxEntry(): Promise<MediaUnderstandingModelConfig | null> {
|
||||
if (!(await hasBinary("sherpa-onnx-offline"))) return null;
|
||||
if (!(await hasBinary("sherpa-onnx-offline"))) {
|
||||
return null;
|
||||
}
|
||||
const modelDir = process.env.SHERPA_ONNX_MODEL_DIR?.trim();
|
||||
if (!modelDir) return null;
|
||||
if (!modelDir) {
|
||||
return null;
|
||||
}
|
||||
const tokens = path.join(modelDir, "tokens.txt");
|
||||
const encoder = path.join(modelDir, "encoder.onnx");
|
||||
const decoder = path.join(modelDir, "decoder.onnx");
|
||||
const joiner = path.join(modelDir, "joiner.onnx");
|
||||
if (!(await fileExists(tokens))) return null;
|
||||
if (!(await fileExists(encoder))) return null;
|
||||
if (!(await fileExists(decoder))) return null;
|
||||
if (!(await fileExists(joiner))) return null;
|
||||
if (!(await fileExists(tokens))) {
|
||||
return null;
|
||||
}
|
||||
if (!(await fileExists(encoder))) {
|
||||
return null;
|
||||
}
|
||||
if (!(await fileExists(decoder))) {
|
||||
return null;
|
||||
}
|
||||
if (!(await fileExists(joiner))) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
type: "cli",
|
||||
command: "sherpa-onnx-offline",
|
||||
@@ -302,16 +364,22 @@ async function resolveSherpaOnnxEntry(): Promise<MediaUnderstandingModelConfig |
|
||||
|
||||
async function resolveLocalAudioEntry(): Promise<MediaUnderstandingModelConfig | null> {
|
||||
const sherpa = await resolveSherpaOnnxEntry();
|
||||
if (sherpa) return sherpa;
|
||||
if (sherpa) {
|
||||
return sherpa;
|
||||
}
|
||||
const whisperCpp = await resolveLocalWhisperCppEntry();
|
||||
if (whisperCpp) return whisperCpp;
|
||||
if (whisperCpp) {
|
||||
return whisperCpp;
|
||||
}
|
||||
return await resolveLocalWhisperEntry();
|
||||
}
|
||||
|
||||
async function resolveGeminiCliEntry(
|
||||
_capability: MediaUnderstandingCapability,
|
||||
): Promise<MediaUnderstandingModelConfig | null> {
|
||||
if (!(await probeGeminiCli())) return null;
|
||||
if (!(await probeGeminiCli())) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
type: "cli",
|
||||
command: "gemini",
|
||||
@@ -341,10 +409,18 @@ async function resolveKeyEntry(params: {
|
||||
model?: string,
|
||||
): Promise<MediaUnderstandingModelConfig | null> => {
|
||||
const provider = getMediaUnderstandingProvider(providerId, providerRegistry);
|
||||
if (!provider) return null;
|
||||
if (capability === "audio" && !provider.transcribeAudio) return null;
|
||||
if (capability === "image" && !provider.describeImage) return null;
|
||||
if (capability === "video" && !provider.describeVideo) return null;
|
||||
if (!provider) {
|
||||
return null;
|
||||
}
|
||||
if (capability === "audio" && !provider.transcribeAudio) {
|
||||
return null;
|
||||
}
|
||||
if (capability === "image" && !provider.describeImage) {
|
||||
return null;
|
||||
}
|
||||
if (capability === "video" && !provider.describeVideo) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
await resolveApiKeyForProvider({ provider: providerId, cfg, agentDir });
|
||||
return { type: "provider" as const, provider: providerId, model };
|
||||
@@ -357,12 +433,16 @@ async function resolveKeyEntry(params: {
|
||||
const activeProvider = params.activeModel?.provider?.trim();
|
||||
if (activeProvider) {
|
||||
const activeEntry = await checkProvider(activeProvider, params.activeModel?.model);
|
||||
if (activeEntry) return activeEntry;
|
||||
if (activeEntry) {
|
||||
return activeEntry;
|
||||
}
|
||||
}
|
||||
for (const providerId of AUTO_IMAGE_KEY_PROVIDERS) {
|
||||
const model = DEFAULT_IMAGE_MODELS[providerId];
|
||||
const entry = await checkProvider(providerId, model);
|
||||
if (entry) return entry;
|
||||
if (entry) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -371,11 +451,15 @@ async function resolveKeyEntry(params: {
|
||||
const activeProvider = params.activeModel?.provider?.trim();
|
||||
if (activeProvider) {
|
||||
const activeEntry = await checkProvider(activeProvider, params.activeModel?.model);
|
||||
if (activeEntry) return activeEntry;
|
||||
if (activeEntry) {
|
||||
return activeEntry;
|
||||
}
|
||||
}
|
||||
for (const providerId of AUTO_VIDEO_KEY_PROVIDERS) {
|
||||
const entry = await checkProvider(providerId, undefined);
|
||||
if (entry) return entry;
|
||||
if (entry) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -383,11 +467,15 @@ async function resolveKeyEntry(params: {
|
||||
const activeProvider = params.activeModel?.provider?.trim();
|
||||
if (activeProvider) {
|
||||
const activeEntry = await checkProvider(activeProvider, params.activeModel?.model);
|
||||
if (activeEntry) return activeEntry;
|
||||
if (activeEntry) {
|
||||
return activeEntry;
|
||||
}
|
||||
}
|
||||
for (const providerId of AUTO_AUDIO_KEY_PROVIDERS) {
|
||||
const entry = await checkProvider(providerId, undefined);
|
||||
if (entry) return entry;
|
||||
if (entry) {
|
||||
return entry;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@@ -400,15 +488,23 @@ async function resolveAutoEntries(params: {
|
||||
activeModel?: ActiveMediaModel;
|
||||
}): Promise<MediaUnderstandingModelConfig[]> {
|
||||
const activeEntry = await resolveActiveModelEntry(params);
|
||||
if (activeEntry) return [activeEntry];
|
||||
if (activeEntry) {
|
||||
return [activeEntry];
|
||||
}
|
||||
if (params.capability === "audio") {
|
||||
const localAudio = await resolveLocalAudioEntry();
|
||||
if (localAudio) return [localAudio];
|
||||
if (localAudio) {
|
||||
return [localAudio];
|
||||
}
|
||||
}
|
||||
const gemini = await resolveGeminiCliEntry(params.capability);
|
||||
if (gemini) return [gemini];
|
||||
if (gemini) {
|
||||
return [gemini];
|
||||
}
|
||||
const keys = await resolveKeyEntry(params);
|
||||
if (keys) return [keys];
|
||||
if (keys) {
|
||||
return [keys];
|
||||
}
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -419,11 +515,17 @@ export async function resolveAutoImageModel(params: {
|
||||
}): Promise<ActiveMediaModel | null> {
|
||||
const providerRegistry = buildProviderRegistry();
|
||||
const toActive = (entry: MediaUnderstandingModelConfig | null): ActiveMediaModel | null => {
|
||||
if (!entry || entry.type === "cli") return null;
|
||||
if (!entry || entry.type === "cli") {
|
||||
return null;
|
||||
}
|
||||
const provider = entry.provider;
|
||||
if (!provider) return null;
|
||||
if (!provider) {
|
||||
return null;
|
||||
}
|
||||
const model = entry.model ?? DEFAULT_IMAGE_MODELS[provider];
|
||||
if (!model) return null;
|
||||
if (!model) {
|
||||
return null;
|
||||
}
|
||||
return { provider, model };
|
||||
};
|
||||
const activeEntry = await resolveActiveModelEntry({
|
||||
@@ -434,7 +536,9 @@ export async function resolveAutoImageModel(params: {
|
||||
activeModel: params.activeModel,
|
||||
});
|
||||
const resolvedActive = toActive(activeEntry);
|
||||
if (resolvedActive) return resolvedActive;
|
||||
if (resolvedActive) {
|
||||
return resolvedActive;
|
||||
}
|
||||
const keyEntry = await resolveKeyEntry({
|
||||
cfg: params.cfg,
|
||||
agentDir: params.agentDir,
|
||||
@@ -453,14 +557,26 @@ async function resolveActiveModelEntry(params: {
|
||||
activeModel?: ActiveMediaModel;
|
||||
}): Promise<MediaUnderstandingModelConfig | null> {
|
||||
const activeProviderRaw = params.activeModel?.provider?.trim();
|
||||
if (!activeProviderRaw) return null;
|
||||
if (!activeProviderRaw) {
|
||||
return null;
|
||||
}
|
||||
const providerId = normalizeMediaProviderId(activeProviderRaw);
|
||||
if (!providerId) return null;
|
||||
if (!providerId) {
|
||||
return null;
|
||||
}
|
||||
const provider = getMediaUnderstandingProvider(providerId, params.providerRegistry);
|
||||
if (!provider) return null;
|
||||
if (params.capability === "audio" && !provider.transcribeAudio) return null;
|
||||
if (params.capability === "image" && !provider.describeImage) return null;
|
||||
if (params.capability === "video" && !provider.describeVideo) return null;
|
||||
if (!provider) {
|
||||
return null;
|
||||
}
|
||||
if (params.capability === "audio" && !provider.transcribeAudio) {
|
||||
return null;
|
||||
}
|
||||
if (params.capability === "image" && !provider.describeImage) {
|
||||
return null;
|
||||
}
|
||||
if (params.capability === "video" && !provider.describeVideo) {
|
||||
return null;
|
||||
}
|
||||
try {
|
||||
await resolveApiKeyForProvider({
|
||||
provider: providerId,
|
||||
@@ -479,7 +595,9 @@ async function resolveActiveModelEntry(params: {
|
||||
|
||||
function trimOutput(text: string, maxChars?: number): string {
|
||||
const trimmed = text.trim();
|
||||
if (!maxChars || trimmed.length <= maxChars) return trimmed;
|
||||
if (!maxChars || trimmed.length <= maxChars) {
|
||||
return trimmed;
|
||||
}
|
||||
return trimmed.slice(0, maxChars).trim();
|
||||
}
|
||||
|
||||
@@ -491,7 +609,9 @@ function findArgValue(args: string[], keys: string[]): string | undefined {
|
||||
for (let i = 0; i < args.length; i += 1) {
|
||||
if (keys.includes(args[i] ?? "")) {
|
||||
const value = args[i + 1];
|
||||
if (value) return value;
|
||||
if (value) {
|
||||
return value;
|
||||
}
|
||||
}
|
||||
}
|
||||
return undefined;
|
||||
@@ -504,17 +624,25 @@ function hasArg(args: string[], keys: string[]): boolean {
|
||||
function resolveWhisperOutputPath(args: string[], mediaPath: string): string | null {
|
||||
const outputDir = findArgValue(args, ["--output_dir", "-o"]);
|
||||
const outputFormat = findArgValue(args, ["--output_format"]);
|
||||
if (!outputDir || !outputFormat) return null;
|
||||
if (!outputDir || !outputFormat) {
|
||||
return null;
|
||||
}
|
||||
const formats = outputFormat.split(",").map((value) => value.trim());
|
||||
if (!formats.includes("txt")) return null;
|
||||
if (!formats.includes("txt")) {
|
||||
return null;
|
||||
}
|
||||
const base = path.parse(mediaPath).name;
|
||||
return path.join(outputDir, `${base}.txt`);
|
||||
}
|
||||
|
||||
function resolveWhisperCppOutputPath(args: string[]): string | null {
|
||||
if (!hasArg(args, ["-otxt", "--output-txt"])) return null;
|
||||
if (!hasArg(args, ["-otxt", "--output-txt"])) {
|
||||
return null;
|
||||
}
|
||||
const outputBase = findArgValue(args, ["-of", "--output-file"]);
|
||||
if (!outputBase) return null;
|
||||
if (!outputBase) {
|
||||
return null;
|
||||
}
|
||||
return `${outputBase}.txt`;
|
||||
}
|
||||
|
||||
@@ -534,18 +662,24 @@ async function resolveCliOutput(params: {
|
||||
if (fileOutput && (await fileExists(fileOutput))) {
|
||||
try {
|
||||
const content = await fs.readFile(fileOutput, "utf8");
|
||||
if (content.trim()) return content.trim();
|
||||
if (content.trim()) {
|
||||
return content.trim();
|
||||
}
|
||||
} catch {}
|
||||
}
|
||||
|
||||
if (commandId === "gemini") {
|
||||
const response = extractGeminiResponse(params.stdout);
|
||||
if (response) return response;
|
||||
if (response) {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
if (commandId === "sherpa-onnx-offline") {
|
||||
const response = extractSherpaOnnxText(params.stdout);
|
||||
if (response) return response;
|
||||
if (response) {
|
||||
return response;
|
||||
}
|
||||
}
|
||||
|
||||
return params.stdout.trim();
|
||||
@@ -556,10 +690,14 @@ type ProviderQuery = Record<string, string | number | boolean>;
|
||||
function normalizeProviderQuery(
|
||||
options?: Record<string, string | number | boolean>,
|
||||
): ProviderQuery | undefined {
|
||||
if (!options) return undefined;
|
||||
if (!options) {
|
||||
return undefined;
|
||||
}
|
||||
const query: ProviderQuery = {};
|
||||
for (const [key, value] of Object.entries(options)) {
|
||||
if (value === undefined) continue;
|
||||
if (value === undefined) {
|
||||
continue;
|
||||
}
|
||||
query[key] = value;
|
||||
}
|
||||
return Object.keys(query).length > 0 ? query : undefined;
|
||||
@@ -570,11 +708,19 @@ function buildDeepgramCompatQuery(options?: {
|
||||
punctuate?: boolean;
|
||||
smartFormat?: boolean;
|
||||
}): ProviderQuery | undefined {
|
||||
if (!options) return undefined;
|
||||
if (!options) {
|
||||
return undefined;
|
||||
}
|
||||
const query: ProviderQuery = {};
|
||||
if (typeof options.detectLanguage === "boolean") query.detect_language = options.detectLanguage;
|
||||
if (typeof options.punctuate === "boolean") query.punctuate = options.punctuate;
|
||||
if (typeof options.smartFormat === "boolean") query.smart_format = options.smartFormat;
|
||||
if (typeof options.detectLanguage === "boolean") {
|
||||
query.detect_language = options.detectLanguage;
|
||||
}
|
||||
if (typeof options.punctuate === "boolean") {
|
||||
query.punctuate = options.punctuate;
|
||||
}
|
||||
if (typeof options.smartFormat === "boolean") {
|
||||
query.smart_format = options.smartFormat;
|
||||
}
|
||||
return Object.keys(query).length > 0 ? query : undefined;
|
||||
}
|
||||
|
||||
@@ -908,7 +1054,9 @@ async function runCliEntry(params: {
|
||||
mediaPath,
|
||||
});
|
||||
const text = trimOutput(resolved, maxChars);
|
||||
if (!text) return null;
|
||||
if (!text) {
|
||||
return null;
|
||||
}
|
||||
return {
|
||||
kind: capability === "audio" ? "audio.transcription" : `${capability}.description`,
|
||||
attachmentIndex: params.attachmentIndex,
|
||||
@@ -964,8 +1112,12 @@ async function runAttachmentEntries(params: {
|
||||
});
|
||||
if (result) {
|
||||
const decision = buildModelDecision({ entry, entryType, outcome: "success" });
|
||||
if (result.provider) decision.provider = result.provider;
|
||||
if (result.model) decision.model = result.model;
|
||||
if (result.provider) {
|
||||
decision.provider = result.provider;
|
||||
}
|
||||
if (result.model) {
|
||||
decision.model = result.model;
|
||||
}
|
||||
attempts.push(decision);
|
||||
return { output: result, attempts };
|
||||
}
|
||||
@@ -1129,7 +1281,9 @@ export async function runCapability(params: {
|
||||
entries: resolvedEntries,
|
||||
config,
|
||||
});
|
||||
if (output) outputs.push(output);
|
||||
if (output) {
|
||||
outputs.push(output);
|
||||
}
|
||||
attachmentDecisions.push({
|
||||
attachmentIndex: attachment.index,
|
||||
attempts,
|
||||
|
||||
Reference in New Issue
Block a user