@aj-archipelago/cortex
Version:
Cortex is a GraphQL API for AI. It provides a simple, extensible interface for using AI services from OpenAI, Azure and others.
140 lines (120 loc) • 5.35 kB
JavaScript
import { fulfillWithTimeout } from '../lib/promiser.js';
import { PathwayResolver } from './pathwayResolver.js';
import CortexResponse from '../lib/cortexResponse.js';
import logger, { withRequestLoggingDisabled } from '../lib/logger.js';
import { sanitizeBase64 } from '../lib/util.js';
import { resolveClientToolCallback } from './clientToolCallbacks.js';
// This resolver uses standard parameters required by Apollo server:
// (parent, args, contextValue, info)
const rootResolver = async (parent, args, contextValue, info) => {
const { config, pathway } = contextValue;
const { temperature, enableGraphqlCache } = pathway;
// Turn on graphql caching if enableGraphqlCache true and temperature is 0
if (enableGraphqlCache && temperature == 0) { // ||
info.cacheControl.setCacheHint({ maxAge: 60 * 60 * 24, scope: 'PUBLIC' });
}
const pathwayResolver = new PathwayResolver({ config, pathway, args });
contextValue.pathwayResolver = pathwayResolver;
// Execute the request with timeout
let result = null;
try {
const execWithTimeout = () => fulfillWithTimeout(pathway.resolver(parent, args, contextValue, info), pathway.timeout);
if (pathway.requestLoggingDisabled === true) {
result = await withRequestLoggingDisabled(() => execWithTimeout());
} else {
result = await execWithTimeout();
}
} catch (error) {
pathwayResolver.logError(error);
result = error.message || error.toString();
}
if (result instanceof CortexResponse) {
// Use the smart mergeResultData method that handles CortexResponse objects
pathwayResolver.pathwayResultData = pathwayResolver.mergeResultData(result);
result = result.output_text;
}
let resultData = pathwayResolver.pathwayResultData ? JSON.stringify(pathwayResolver.pathwayResultData) : null;
const { warnings, errors, previousResult, savedContextId, tool } = pathwayResolver;
// Add request parameters back as debug - sanitize base64 data before returning
const debug = pathwayResolver.prompts.map(prompt => {
if (!prompt.debugInfo) return '';
try {
// Try to parse entire debugInfo as JSON first (for single JSON object)
try {
const parsed = JSON.parse(prompt.debugInfo);
return JSON.stringify(sanitizeBase64(parsed));
} catch (e) {
// Not a single JSON object, try line-by-line
const lines = prompt.debugInfo.split('\n');
return lines.map(line => {
const trimmed = line.trim();
if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
try {
const parsed = JSON.parse(line);
return JSON.stringify(sanitizeBase64(parsed));
} catch (e) {
// Not valid JSON on this line, return as-is
return line;
}
}
return line;
}).join('\n');
}
} catch (e) {
// If sanitization fails, return original
return prompt.debugInfo;
}
}).join('\n').trim();
return {
debug,
result,
resultData,
warnings,
errors,
previousResult,
tool,
contextId: savedContextId
}
}
// This resolver is used by the root resolver to process the request
const resolver = async (parent, args, contextValue, _info) => {
const { pathwayResolver } = contextValue;
return await pathwayResolver.resolve(args);
}
const cancelRequestResolver = (parent, args, contextValue, _info) => {
const { requestId } = args;
const { requestState } = contextValue;
requestState[requestId] = { canceled: true };
return true
}
const submitClientToolResultResolver = async (parent, args, contextValue, _info) => {
const { requestId, toolCallbackId, result, success } = args;
logger.info(`Received client tool result submission: requestId=${requestId}, toolCallbackId=${toolCallbackId}, success=${success}`);
try {
// Parse the result if it's a string
let parsedResult = result;
try {
parsedResult = JSON.parse(result);
} catch (e) {
// If parsing fails, use the string as-is
}
// Resolve the waiting callback (now async, publishes to Redis if available)
const resolved = await resolveClientToolCallback(toolCallbackId, {
success,
data: parsedResult,
error: !success ? (parsedResult.error || 'Tool execution failed') : null
});
if (!resolved) {
logger.warn(`Failed to publish/resolve callback for toolCallbackId: ${toolCallbackId}`);
return false;
}
logger.info(`Successfully published/resolved client tool callback: ${toolCallbackId}`);
return true;
} catch (error) {
logger.error(`Error in submitClientToolResultResolver: ${error.message}`);
return false;
}
}
export {
resolver, rootResolver, cancelRequestResolver, submitClientToolResultResolver
};