feat(channel): add kitchen sink channel fixture
This commit is contained in:
parent
932a938795
commit
b659e8c02e
@ -49,6 +49,26 @@ assert.equal(hookResult.route, "hook:before_tool_call");
|
||||
assert.equal(hookResult.scenarioId, "image.generate");
|
||||
assert.equal(hookResult.matchedKitchen, true);
|
||||
|
||||
const channel = findRegistration("registerChannel", "kitchen-sink-channel");
|
||||
const channelAccount = channel.config.resolveAccount({}, "local");
|
||||
assert.equal(channelAccount.configured, true);
|
||||
assert.equal(channelAccount.enabled, true);
|
||||
const channelDelivery = await channel.outbound.sendText({
|
||||
cfg: {},
|
||||
to: "kitchen demo",
|
||||
text: "kitchen generate an image",
|
||||
});
|
||||
assert.equal(channelDelivery.channel, "kitchen-sink-channel");
|
||||
assert.equal(channelDelivery.conversationId, "kitchen-demo");
|
||||
assert.equal(channelDelivery.meta.scenarioId, "image.generate");
|
||||
const channelRoute = await channel.messaging.resolveOutboundSessionRoute({
|
||||
cfg: {},
|
||||
agentId: "fixture-agent",
|
||||
target: "kitchen demo",
|
||||
});
|
||||
assert.equal(channelRoute.sessionKey, "kitchen:fixture-agent:kitchen-demo");
|
||||
assert.equal(channelRoute.peer.kind, "direct");
|
||||
|
||||
const imageProvider = findRegistration("registerImageGenerationProvider", "kitchen-sink-image");
|
||||
assert.equal(imageProvider.defaultModel, "kitchen-sink-image-v1");
|
||||
|
||||
|
||||
@ -67,11 +67,12 @@ export const apiSurfaceProbeFailures = [];
|
||||
|
||||
function payloadFor(name) {
|
||||
const id = name.replace(/^register/, "").replace(/[A-Z]/g, (letter, index) => (index === 0 ? "" : "-") + letter.toLowerCase()) || "probe";
|
||||
const probeId = name === "registerChannel" ? "kitchen-sink-channel-probe" : "kitchen-sink-" + id;
|
||||
return {
|
||||
id: "kitchen-sink-" + id,
|
||||
name: "kitchen-sink-" + id,
|
||||
id: probeId,
|
||||
name: probeId,
|
||||
description: "Kitchen-sink no-op probe for " + name + ".",
|
||||
command: "kitchen-sink-" + id,
|
||||
command: probeId,
|
||||
path: "/kitchen-sink/" + id,
|
||||
method: "POST",
|
||||
inputSchema: objectSchema(),
|
||||
|
||||
@ -56,11 +56,12 @@ export const apiSurfaceProbeFailures = [];
|
||||
|
||||
function payloadFor(name) {
|
||||
const id = name.replace(/^register/, "").replace(/[A-Z]/g, (letter, index) => (index === 0 ? "" : "-") + letter.toLowerCase()) || "probe";
|
||||
const probeId = name === "registerChannel" ? "kitchen-sink-channel-probe" : "kitchen-sink-" + id;
|
||||
return {
|
||||
id: "kitchen-sink-" + id,
|
||||
name: "kitchen-sink-" + id,
|
||||
id: probeId,
|
||||
name: probeId,
|
||||
description: "Kitchen-sink no-op probe for " + name + ".",
|
||||
command: "kitchen-sink-" + id,
|
||||
command: probeId,
|
||||
path: "/kitchen-sink/" + id,
|
||||
method: "POST",
|
||||
inputSchema: objectSchema(),
|
||||
|
||||
@ -2,22 +2,27 @@ import {
|
||||
DEFAULT_IMAGE_MODEL,
|
||||
DEFAULT_MEDIA_MODEL,
|
||||
DEFAULT_TEXT_MODEL,
|
||||
CHANNEL_ACCOUNT_ID,
|
||||
CHANNEL_ID,
|
||||
IMAGE_PROVIDER_ID,
|
||||
MEDIA_PROVIDER_ID,
|
||||
PLUGIN_ID,
|
||||
TEXT_PROVIDER_ID,
|
||||
WEB_FETCH_PROVIDER_ID,
|
||||
WEB_SEARCH_PROVIDER_ID,
|
||||
createKitchenChannelDelivery,
|
||||
createKitchenScenarioRuntime,
|
||||
createKitchenSinkImageAsset,
|
||||
createKitchenTextStream,
|
||||
extractInteractiveText,
|
||||
kitchenChannelAccount,
|
||||
kitchenImageDescription,
|
||||
kitchenPromptGuidance,
|
||||
kitchenSearchSchema,
|
||||
kitchenTextModelDefinition,
|
||||
kitchenTextProviderConfig,
|
||||
kitchenToolSchema,
|
||||
normalizeKitchenTarget,
|
||||
readPrompt,
|
||||
readQuery,
|
||||
readUrl,
|
||||
@ -39,6 +44,7 @@ export function registerKitchenSinkRuntime(api, options = {}) {
|
||||
optionalRegister(api, "registerInteractiveHandler", () =>
|
||||
api.registerInteractiveHandler(buildKitchenInteractiveHandler(runtime)),
|
||||
);
|
||||
optionalRegister(api, "registerChannel", () => api.registerChannel(buildKitchenChannel()));
|
||||
optionalRegister(api, "registerTool", () => api.registerTool(buildKitchenImageTool(runtime)));
|
||||
optionalRegister(api, "registerTool", () => api.registerTool(buildKitchenTextTool(runtime)));
|
||||
optionalRegister(api, "registerTool", () => api.registerTool(buildKitchenSearchTool()));
|
||||
@ -147,6 +153,84 @@ function buildKitchenSearchTool() {
|
||||
};
|
||||
}
|
||||
|
||||
function buildKitchenChannel() {
|
||||
return {
|
||||
id: CHANNEL_ID,
|
||||
meta: {
|
||||
id: CHANNEL_ID,
|
||||
label: "Kitchen Sink",
|
||||
selectionLabel: "Kitchen Sink",
|
||||
docsPath: "/plugins/kitchen-sink",
|
||||
docsLabel: "Kitchen Sink",
|
||||
blurb: "Credential-free channel fixture for deterministic Kitchen Sink conversations.",
|
||||
aliases: ["kitchen", "kitchen-sink"],
|
||||
exposure: { configured: true, setup: true, docs: true },
|
||||
showConfigured: true,
|
||||
showInSetup: true,
|
||||
},
|
||||
capabilities: {
|
||||
chatTypes: ["direct", "group", "channel"],
|
||||
media: true,
|
||||
nativeCommands: true,
|
||||
reply: true,
|
||||
threads: true,
|
||||
},
|
||||
config: {
|
||||
listAccountIds: () => [CHANNEL_ACCOUNT_ID],
|
||||
defaultAccountId: () => CHANNEL_ACCOUNT_ID,
|
||||
resolveAccount: (_cfg, accountId) => kitchenChannelAccount(accountId || CHANNEL_ACCOUNT_ID),
|
||||
isEnabled: () => true,
|
||||
isConfigured: () => true,
|
||||
describeAccount: (account) => kitchenChannelAccount(account.accountId),
|
||||
resolveDefaultTo: () => "kitchen",
|
||||
},
|
||||
status: {
|
||||
defaultRuntime: kitchenChannelAccount(),
|
||||
probeAccount: async ({ account }) => ({
|
||||
ok: true,
|
||||
accountId: account.accountId,
|
||||
scenarioId: "channel.probe",
|
||||
}),
|
||||
buildAccountSnapshot: ({ account }) => kitchenChannelAccount(account.accountId),
|
||||
},
|
||||
outbound: {
|
||||
deliveryMode: "direct",
|
||||
textChunkLimit: 2000,
|
||||
sendText: async (ctx) =>
|
||||
createKitchenChannelDelivery({ kind: "text", text: ctx?.text, to: ctx?.to }),
|
||||
sendMedia: async (ctx) =>
|
||||
createKitchenChannelDelivery({ kind: "media", text: ctx?.mediaUrl || ctx?.text, to: ctx?.to }),
|
||||
},
|
||||
messaging: {
|
||||
normalizeTarget: (raw) => normalizeKitchenTarget(raw),
|
||||
parseExplicitTarget: ({ raw }) => ({
|
||||
to: normalizeKitchenTarget(raw),
|
||||
chatType: "direct",
|
||||
}),
|
||||
inferTargetChatType: () => "direct",
|
||||
resolveOutboundSessionRoute: ({ agentId, target, threadId }) => {
|
||||
const to = normalizeKitchenTarget(target);
|
||||
return {
|
||||
sessionKey: `kitchen:${agentId || "agent"}:${to}`,
|
||||
baseSessionKey: `kitchen:${agentId || "agent"}:${to}`,
|
||||
peer: { kind: "direct", id: to },
|
||||
chatType: "direct",
|
||||
from: CHANNEL_ACCOUNT_ID,
|
||||
to,
|
||||
threadId: threadId || undefined,
|
||||
};
|
||||
},
|
||||
},
|
||||
agentPrompt: {
|
||||
messageToolHints: () => kitchenPromptGuidance(),
|
||||
messageToolCapabilities: () => [
|
||||
"Kitchen Sink channel accepts deterministic dry messages prefixed with kitchen.",
|
||||
"Kitchen Sink channel can deliver text and media without external credentials.",
|
||||
],
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function buildKitchenImageProvider(runtime) {
|
||||
return {
|
||||
id: IMAGE_PROVIDER_ID,
|
||||
|
||||
@ -4,6 +4,8 @@ export const MEDIA_PROVIDER_ID = "kitchen-sink-media";
|
||||
export const TEXT_PROVIDER_ID = "kitchen-sink-llm";
|
||||
export const WEB_SEARCH_PROVIDER_ID = "kitchen-sink-search";
|
||||
export const WEB_FETCH_PROVIDER_ID = "kitchen-sink-fetch";
|
||||
export const CHANNEL_ID = "kitchen-sink-channel";
|
||||
export const CHANNEL_ACCOUNT_ID = "local";
|
||||
export const DEFAULT_IMAGE_MODEL = "kitchen-sink-image-v1";
|
||||
export const DEFAULT_MEDIA_MODEL = "kitchen-sink-vision-v1";
|
||||
export const DEFAULT_TEXT_MODEL = "kitchen-sink-text-v1";
|
||||
@ -121,6 +123,42 @@ export function kitchenPromptGuidance() {
|
||||
];
|
||||
}
|
||||
|
||||
export function createKitchenChannelDelivery({ kind = "text", text = "", to = "kitchen" }) {
|
||||
const normalizedTo = normalizeKitchenTarget(to);
|
||||
const id = `ks_channel_${stableHash(`${kind}:${normalizedTo}:${text}`).slice(0, 10)}`;
|
||||
return {
|
||||
channel: CHANNEL_ID,
|
||||
messageId: id,
|
||||
conversationId: normalizedTo,
|
||||
channelId: normalizedTo,
|
||||
timestamp: Date.now(),
|
||||
meta: {
|
||||
kitchenSink: true,
|
||||
pluginId: PLUGIN_ID,
|
||||
scenarioId: inferKitchenScenario({ text }),
|
||||
kind,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
export function kitchenChannelAccount(accountId = CHANNEL_ACCOUNT_ID) {
|
||||
return {
|
||||
accountId: accountId || CHANNEL_ACCOUNT_ID,
|
||||
name: "Kitchen Sink Local",
|
||||
enabled: true,
|
||||
configured: true,
|
||||
statusState: "fixture",
|
||||
linked: true,
|
||||
running: true,
|
||||
connected: true,
|
||||
mode: "local",
|
||||
};
|
||||
}
|
||||
|
||||
export function normalizeKitchenTarget(raw) {
|
||||
return String(raw ?? "").replace(/^kitchen:/i, "").replace(/\s+/g, "-").trim() || "kitchen";
|
||||
}
|
||||
|
||||
export async function runKitchenCommand(runtime, args) {
|
||||
const phrase = String(args ?? "").trim();
|
||||
if (/\b(image|picture|draw|generate)\b/i.test(phrase)) {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user