@inflexa-ai/harness 0.12.0 → 0.14.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/agents/conversation-agent.d.ts +9 -15
- package/dist/agents/conversation-agent.js +32 -31
- package/dist/agents/sandbox/cheminformatics-agent.js +1 -1
- package/dist/agents/sandbox/chromatin-agent.js +1 -1
- package/dist/agents/sandbox/dna-methylation-agent.js +1 -1
- package/dist/agents/sandbox/drug-repurposing-agent.js +5 -10
- package/dist/agents/sandbox/enrichment-agent.js +1 -1
- package/dist/agents/sandbox/genomic-variant-agent.js +1 -11
- package/dist/agents/sandbox/immune-profiling-agent.js +1 -1
- package/dist/agents/sandbox/index.js +0 -3
- package/dist/agents/sandbox/multi-omics-integration-agent.js +1 -1
- package/dist/agents/sandbox/network-agent.js +1 -1
- package/dist/agents/sandbox/pkpd-clinical-response-agent.js +3 -6
- package/dist/agents/sandbox/scientific-executor.js +1 -1
- package/dist/agents/sandbox/shared.d.ts +2 -2
- package/dist/agents/sandbox/shared.js +8 -14
- package/dist/agents/sandbox/translational-safety-agent.js +4 -7
- package/dist/agents/sandbox/types.d.ts +5 -5
- package/dist/agents/sandbox/types.js +4 -4
- package/dist/app/chat-turn.js +4 -1
- package/dist/app/message-assembly.d.ts +5 -2
- package/dist/app/message-assembly.js +7 -2
- package/dist/app/run-activity.d.ts +13 -0
- package/dist/app/run-activity.js +51 -0
- package/dist/config/resource-limits.d.ts +0 -7
- package/dist/config/resource-limits.js +0 -1
- package/dist/contracts/target-dossier.d.ts +10 -10
- package/dist/execution/dbos-run-launcher.js +1 -26
- package/dist/execution/report-runner.js +1 -1
- package/dist/execution/run-authorizer.d.ts +1 -1
- package/dist/execution/run-authorizer.js +1 -1
- package/dist/execution/run-event-parts.d.ts +44 -0
- package/dist/execution/run-event-parts.js +0 -0
- package/dist/execution/run-event-stream.d.ts +91 -0
- package/dist/execution/run-event-stream.js +212 -0
- package/dist/execution/run-launcher.d.ts +2 -18
- package/dist/execution/run-launcher.js +2 -4
- package/dist/execution/run-synthesis.js +1 -1
- package/dist/index.d.ts +7 -4
- package/dist/index.js +23 -6
- package/dist/input-sanitization.d.ts +14 -4
- package/dist/input-sanitization.js +27 -4
- package/dist/lib/storage.js +4 -1
- package/dist/loop/budget-exceeded.d.ts +18 -2
- package/dist/loop/budget-exceeded.js +49 -2
- package/dist/loop/run-agent.d.ts +22 -1
- package/dist/loop/run-agent.js +51 -5
- package/dist/loop/run-to-terminal.d.ts +0 -2
- package/dist/loop/run-to-terminal.js +8 -1
- package/dist/memory/ai-sdk-message-storage.d.ts +37 -4
- package/dist/memory/ai-sdk-message-storage.js +45 -4
- package/dist/memory/content-to-cortex.js +19 -3
- package/dist/memory/message-backfill.js +1 -1
- package/dist/memory/reconstruct-cards.d.ts +1 -1
- package/dist/memory/reconstruct-cards.js +7 -5
- package/dist/memory/thread-history.js +72 -8
- package/dist/prompts/conversation.d.ts +1 -1
- package/dist/prompts/conversation.js +41 -15
- package/dist/prompts/literature-reviewer.d.ts +1 -1
- package/dist/prompts/literature-reviewer.js +16 -14
- package/dist/prompts/planner.d.ts +3 -0
- package/dist/prompts/planner.js +16 -21
- package/dist/prompts/sandbox/drug-repurposing-agent.d.ts +1 -1
- package/dist/prompts/sandbox/drug-repurposing-agent.js +31 -22
- package/dist/prompts/sandbox/multi-omics-integration-agent.d.ts +1 -1
- package/dist/prompts/sandbox/multi-omics-integration-agent.js +9 -10
- package/dist/prompts/sandbox/pkpd-clinical-response-agent.d.ts +1 -1
- package/dist/prompts/sandbox/pkpd-clinical-response-agent.js +8 -7
- package/dist/prompts/sandbox/translational-safety-agent.d.ts +1 -1
- package/dist/prompts/sandbox/translational-safety-agent.js +20 -14
- package/dist/prompts/sandbox-standards.d.ts +4 -4
- package/dist/prompts/sandbox-standards.js +5 -5
- package/dist/prompts/synthesis-agent.d.ts +1 -1
- package/dist/prompts/synthesis-agent.js +7 -7
- package/dist/providers/errors.d.ts +7 -0
- package/dist/providers/errors.js +88 -3
- package/dist/runtime/assemble.d.ts +5 -10
- package/dist/runtime/assemble.js +0 -4
- package/dist/runtime/boot.d.ts +2 -2
- package/dist/runtime/boot.js +1 -1
- package/dist/runtime/dbos.d.ts +4 -5
- package/dist/runtime/dbos.js +6 -7
- package/dist/runtime/pools.d.ts +15 -1
- package/dist/runtime/pools.js +15 -1
- package/dist/sandbox/mount-plan.d.ts +1 -1
- package/dist/sandbox/sandbox-error.d.ts +1 -1
- package/dist/sandbox/sandbox-error.js +1 -1
- package/dist/sandbox/sandbox-step-translate.d.ts +15 -2
- package/dist/sandbox/sandbox-step-translate.js +17 -2
- package/dist/sandbox/types.d.ts +1 -1
- package/dist/schemas/validate-plan.d.ts +1 -1
- package/dist/schemas/workflow-state.d.ts +2 -2
- package/dist/schemas/workflow-state.js +2 -2
- package/dist/state/data-profile.d.ts +53 -1
- package/dist/state/data-profile.js +66 -6
- package/dist/state/index.d.ts +6 -6
- package/dist/state/index.js +4 -4
- package/dist/state/init.js +37 -2
- package/dist/state/plans.js +1 -1
- package/dist/state/runs.d.ts +49 -0
- package/dist/state/runs.js +150 -0
- package/dist/tasks/data-profile-activity.d.ts +87 -0
- package/dist/tasks/data-profile-activity.js +56 -0
- package/dist/tasks/data-profile.js +66 -4
- package/dist/tools/__fixtures__/tool-context.js +1 -0
- package/dist/tools/ad-hoc-router.d.ts +31 -0
- package/dist/tools/ad-hoc-router.js +132 -0
- package/dist/tools/analysis-invocation.d.ts +4 -0
- package/dist/tools/analysis-invocation.js +17 -0
- package/dist/tools/bio/chembl.d.ts +1 -1
- package/dist/tools/bio/chembl.js +51 -54
- package/dist/tools/bio/comptox.js +35 -33
- package/dist/tools/bio/drug-gene-interactions.d.ts +76 -0
- package/dist/tools/bio/drug-gene-interactions.js +175 -0
- package/dist/tools/bio/gene-disease-evidence.d.ts +61 -0
- package/dist/tools/bio/gene-disease-evidence.js +187 -0
- package/dist/tools/bio/gene-preclinical-profile.d.ts +58 -0
- package/dist/tools/bio/gene-preclinical-profile.js +125 -0
- package/dist/tools/bio/index.d.ts +23 -16
- package/dist/tools/bio/index.js +29 -22
- package/dist/tools/bio/keys.d.ts +16 -9
- package/dist/tools/bio/keys.js +22 -13
- package/dist/tools/bio/lookup-annotation.d.ts +48 -0
- package/dist/tools/bio/lookup-annotation.js +173 -0
- package/dist/tools/bio/opentargets.d.ts +7 -11
- package/dist/tools/bio/opentargets.js +24 -35
- package/dist/tools/bio/pubchem.js +6 -4
- package/dist/tools/bio/pubmed.d.ts +15 -7
- package/dist/tools/bio/pubmed.js +79 -20
- package/dist/tools/bio/search-clinical-trials.d.ts +20 -1
- package/dist/tools/bio/search-clinical-trials.js +26 -5
- package/dist/tools/bio/search-faers.js +9 -3
- package/dist/tools/bio/search-gene.d.ts +0 -1
- package/dist/tools/bio/search-gene.js +4 -5
- package/dist/tools/bio/search-geo-datasets.js +10 -4
- package/dist/tools/bio/search-interactions.d.ts +8 -4
- package/dist/tools/bio/search-interactions.js +28 -12
- package/dist/tools/bio/target-safety.d.ts +59 -0
- package/dist/tools/bio/target-safety.js +191 -0
- package/dist/tools/define-tool.d.ts +6 -2
- package/dist/tools/define-tool.js +1 -1
- package/dist/tools/execute-analysis.d.ts +44 -0
- package/dist/tools/execute-analysis.js +252 -0
- package/dist/tools/iterate-report.js +7 -6
- package/dist/tools/lib/dgidb-client.d.ts +49 -0
- package/dist/tools/lib/dgidb-client.js +168 -0
- package/dist/tools/lib/disgenet-client.d.ts +56 -0
- package/dist/tools/lib/disgenet-client.js +59 -0
- package/dist/tools/lib/drugbank-client.d.ts +83 -0
- package/dist/tools/lib/drugbank-client.js +95 -0
- package/dist/tools/lib/ensembl-client.d.ts +4 -1
- package/dist/tools/lib/ensembl-client.js +5 -3
- package/dist/tools/lib/gwas-catalog-client.d.ts +60 -0
- package/dist/tools/lib/gwas-catalog-client.js +136 -0
- package/dist/tools/lib/pharmgkb-client.d.ts +22 -0
- package/dist/tools/lib/pharmgkb-client.js +32 -0
- package/dist/tools/lib/pubmed-client.d.ts +57 -4
- package/dist/tools/lib/pubmed-client.js +57 -3
- package/dist/tools/lib/string-client.d.ts +25 -4
- package/dist/tools/lib/string-client.js +22 -5
- package/dist/tools/research/generate-plan.d.ts +16 -9
- package/dist/tools/research/generate-plan.js +116 -70
- package/dist/tools/research/inspect-data-profile.d.ts +31 -6
- package/dist/tools/research/inspect-data-profile.js +23 -7
- package/dist/tools/research/inspect-run.d.ts +33 -15
- package/dist/tools/research/inspect-run.js +155 -66
- package/dist/tools/research/literature-reviewer.js +7 -11
- package/dist/workflows/execute-analysis.d.ts +68 -5
- package/dist/workflows/execute-analysis.js +76 -7
- package/dist/workflows/sandbox-step.js +1 -11
- package/dist/workflows/target-assessment/decisions/index.d.ts +1 -1
- package/dist/workflows/target-assessment/phase4-assemble.d.ts +1 -1
- package/dist/workflows/target-assessment/steps/phase2-aggregate.d.ts +2 -2
- package/dist/workflows/target-assessment/steps/phase3-aggregate.d.ts +1 -1
- package/package.json +1 -1
- package/dist/agents/sandbox/ephemeral-executor.d.ts +0 -9
- package/dist/agents/sandbox/ephemeral-executor.js +0 -20
- package/dist/execution/ephemeral-runner.d.ts +0 -98
- package/dist/execution/ephemeral-runner.js +0 -189
- package/dist/prompts/sandbox/ephemeral-executor.d.ts +0 -1
- package/dist/prompts/sandbox/ephemeral-executor.js +0 -66
- package/dist/tools/bio/check-safety-panel.d.ts +0 -35
- package/dist/tools/bio/check-safety-panel.js +0 -76
- package/dist/tools/bio/get-impc-ko-profile.d.ts +0 -9
- package/dist/tools/bio/get-impc-ko-profile.js +0 -23
- package/dist/tools/bio/lookup-go-term.d.ts +0 -31
- package/dist/tools/bio/lookup-go-term.js +0 -116
- package/dist/tools/bio/search-bgee-expression.d.ts +0 -10
- package/dist/tools/bio/search-bgee-expression.js +0 -30
- package/dist/tools/bio/search-clinvar.d.ts +0 -13
- package/dist/tools/bio/search-clinvar.js +0 -28
- package/dist/tools/bio/search-dgidb.d.ts +0 -53
- package/dist/tools/bio/search-dgidb.js +0 -213
- package/dist/tools/bio/search-disgenet.d.ts +0 -30
- package/dist/tools/bio/search-disgenet.js +0 -84
- package/dist/tools/bio/search-drugbank.d.ts +0 -38
- package/dist/tools/bio/search-drugbank.js +0 -109
- package/dist/tools/bio/search-gwas-catalog.d.ts +0 -33
- package/dist/tools/bio/search-gwas-catalog.js +0 -172
- package/dist/tools/bio/search-pathway.d.ts +0 -12
- package/dist/tools/bio/search-pathway.js +0 -32
- package/dist/tools/bio/search-pharmgkb.d.ts +0 -17
- package/dist/tools/bio/search-pharmgkb.js +0 -52
- package/dist/tools/execute-plan.d.ts +0 -64
- package/dist/tools/execute-plan.js +0 -211
- package/dist/tools/run-ephemeral.d.ts +0 -33
- package/dist/tools/run-ephemeral.js +0 -65
|
@@ -33,7 +33,8 @@ import { generateExecutionId } from "../sandbox/execution-id.js";
|
|
|
33
33
|
import { mintSandboxIdentity } from "../sandbox/identity.js";
|
|
34
34
|
import { createVectorStore } from "../state/vector-store.js";
|
|
35
35
|
import { ProfilerOutputSchema } from "../schemas/data-profile-schemas.js";
|
|
36
|
-
import { completeDataProfile, failDataProfile, loadDataProfileStatus, loadSeedInputFileIds, tryRerunDataProfile, tryStartDataProfile, upsertArtifacts, } from "../state/index.js";
|
|
36
|
+
import { completeDataProfile, failDataProfile, loadDataProfileStatus, loadSeedInputFileIds, recordDataProfileWorkflowId, tryRerunDataProfile, tryStartDataProfile, upsertArtifacts, } from "../state/index.js";
|
|
37
|
+
import { createProfileActivityEmitter } from "./data-profile-activity.js";
|
|
37
38
|
import { ensureSearchIndex, searchIndexName } from "../workspace/search-config.js";
|
|
38
39
|
/** Synthetic run/step literal for the data-profile workflow. */
|
|
39
40
|
const DATA_PROFILE_RUN_LITERAL = "data-profile";
|
|
@@ -135,7 +136,29 @@ export async function runDataProfileBody(input, deps) {
|
|
|
135
136
|
// Cortex-owned and must be revoked here.
|
|
136
137
|
const { analysisId, runSession, ownsMandate = true, stagedInputs } = input; // oss-core-managed-ok
|
|
137
138
|
const authorization = { runSession, ownsMandate }; // oss-core-managed-ok
|
|
139
|
+
// The profile's activity channel. `DBOS.writeStream` is body-only, so the write is bound here
|
|
140
|
+
// while every phase and phrase lives in the emitter — the phrases are the observable contract of
|
|
141
|
+
// this capability, and a body that composed its own strings is a body they can drift from.
|
|
142
|
+
// The frame is the workflow's synthetic one: both values are constants shared by every analysis,
|
|
143
|
+
// so they identify nothing and consumers are required not to key on them.
|
|
144
|
+
const activity = createProfileActivityEmitter((part) => DBOS.writeStream("events", part), { runId: DATA_PROFILE_RUN_LITERAL, stepId: DATA_PROFILE_STEP_LITERAL }, logger);
|
|
138
145
|
try {
|
|
146
|
+
// Record which workflow owns this profile before anything else, so a consumer that resolves
|
|
147
|
+
// the id from the ledger can subscribe from the earliest possible moment. Guarded on a real
|
|
148
|
+
// ambient id: this body is also driven directly (outside a workflow), where the synthetic
|
|
149
|
+
// fallback used further down names no workflow at all and must never reach the ledger.
|
|
150
|
+
const ownWorkflowId = DBOS.workflowID;
|
|
151
|
+
if (ownWorkflowId !== undefined) {
|
|
152
|
+
await DBOS.runStep(async () => {
|
|
153
|
+
// A refused CAS is a normal in-band outcome, not a failure: the row left
|
|
154
|
+
// `running` underneath us (cleared, or stale-expired and re-claimed), which is
|
|
155
|
+
// the ledger correctly moving on. There is nothing to repair, and the profile's
|
|
156
|
+
// own work is unaffected — only its observability is.
|
|
157
|
+
if (!unwrapOrThrow(await recordDataProfileWorkflowId(deps.pool, analysisId, ownWorkflowId))) {
|
|
158
|
+
logger.warn("workflow-id record skipped: ledger row not running (cleared or re-claimed concurrently)");
|
|
159
|
+
}
|
|
160
|
+
}, { name: "record-workflow-id" });
|
|
161
|
+
}
|
|
139
162
|
// 1. Input files are already staged under data/inputs/ (see the data-profile-init spec); the
|
|
140
163
|
// embedder populated the tree and handed us this manifest in the workflow
|
|
141
164
|
// input. The body never downloads.
|
|
@@ -144,6 +167,9 @@ export async function runDataProfileBody(input, deps) {
|
|
|
144
167
|
if (!unwrapOrThrow(await completeDataProfile(deps.pool, analysisId)))
|
|
145
168
|
logTerminalNoop(logger, analysisId, "completion");
|
|
146
169
|
await deps.runAuthorizer.revoke(authorization, "data-profile-completed");
|
|
170
|
+
// This is a terminal completion like any other, so it reports one — a consumer watching
|
|
171
|
+
// an empty-manifest profile sees it settle rather than seeing the stream simply stop.
|
|
172
|
+
await activity.complete();
|
|
147
173
|
return;
|
|
148
174
|
}
|
|
149
175
|
// 2. Register staged files in cortex_artifacts
|
|
@@ -193,6 +219,11 @@ export async function runDataProfileBody(input, deps) {
|
|
|
193
219
|
`All file paths in your response must be relative to the analysis root and must EXACTLY match the paths listed above.`,
|
|
194
220
|
].join("\n");
|
|
195
221
|
logger.info("starting sandbox", { executionId });
|
|
222
|
+
// Reported BEFORE the container exists, deliberately: provisioning is the longest single
|
|
223
|
+
// operation in a profile and it precedes the agent entirely, so a body that reported only
|
|
224
|
+
// from the agent loop would leave the longest wait of all unreported — the same defect the
|
|
225
|
+
// run panel's activity readout was built to remove.
|
|
226
|
+
await activity.sandboxInit();
|
|
196
227
|
const sandbox = await deps.sandboxClient.createSandbox({
|
|
197
228
|
runId: DATA_PROFILE_RUN_LITERAL,
|
|
198
229
|
stepId: DATA_PROFILE_STEP_LITERAL,
|
|
@@ -247,13 +278,27 @@ export async function runDataProfileBody(input, deps) {
|
|
|
247
278
|
const baseAgent = createDataProfilerAgent(sandboxAgentDeps);
|
|
248
279
|
const agentDef = { ...baseAgent, tools: [...baseAgent.tools, submitProfileTool] };
|
|
249
280
|
const signal = new AbortController().signal;
|
|
281
|
+
// Covers the gap between a ready sandbox and the agent's first tool call, which would
|
|
282
|
+
// otherwise still read as `Starting sandbox` long after the container was up. The run
|
|
283
|
+
// path's `Running ${agentId}` serves the same purpose at the same point.
|
|
284
|
+
await activity.agentStarting();
|
|
250
285
|
await runToTerminal(agentDef, [{ role: "user", content: prompt }], childSession, {
|
|
251
286
|
provider: deps.provider,
|
|
252
287
|
signal,
|
|
253
|
-
|
|
288
|
+
// Every tool call the profiler makes becomes live activity. Only `tool-started`
|
|
289
|
+
// is forwarded: the loop's other events (iteration boundaries, model deltas,
|
|
290
|
+
// tool completions) describe machinery rather than work, and the panel this
|
|
291
|
+
// feeds carries a single line whose value is that it always names what is
|
|
292
|
+
// happening now. Awaited in body order, because `DBOS.writeStream` allocates a
|
|
293
|
+
// function id and an unawaited write would race the next operation for the
|
|
294
|
+
// counter and desynchronise the recorded sequence on replay.
|
|
295
|
+
emit: async (event) => {
|
|
296
|
+
if (event.type === "tool-started")
|
|
297
|
+
await activity.forTool(event.name, event.input);
|
|
298
|
+
},
|
|
254
299
|
runStep: durableStep,
|
|
255
|
-
}, {
|
|
256
300
|
resolved: () => capturedProfile !== null,
|
|
301
|
+
}, {
|
|
257
302
|
tools: [submitProfileTool],
|
|
258
303
|
nudge: "You stopped without calling submit_profile, so no profile was " +
|
|
259
304
|
"recorded. Call submit_profile now with structured metadata for " +
|
|
@@ -264,6 +309,11 @@ export async function runDataProfileBody(input, deps) {
|
|
|
264
309
|
}
|
|
265
310
|
const profilerData = capturedProfile;
|
|
266
311
|
// 4. Index into vector store — real files only, consistent type metadata
|
|
312
|
+
//
|
|
313
|
+
// Reported as `indexing`, not `persisting`: the contract defines `persisting` as step
|
|
314
|
+
// bytes uploading to an artifact store, and a profile uploads nothing — its durable
|
|
315
|
+
// products are this vector index and the ledger row.
|
|
316
|
+
await activity.indexing();
|
|
267
317
|
const profilerByPath = new Map(profilerData.files.map((f) => [f.path, f]));
|
|
268
318
|
const normPath = (p) => p
|
|
269
319
|
.replace(/\/+/g, "/")
|
|
@@ -326,6 +376,13 @@ export async function runDataProfileBody(input, deps) {
|
|
|
326
376
|
logTerminalNoop(logger, analysisId, "completion");
|
|
327
377
|
}
|
|
328
378
|
await deps.runAuthorizer.revoke(authorization, "data-profile-completed");
|
|
379
|
+
// LAST statement of the success path, and that placement is the guarantee that exactly
|
|
380
|
+
// one terminal activity is ever emitted. Everything that could still throw — the ledger
|
|
381
|
+
// write above, the revoke — is already behind us, so any failure on the way here reaches
|
|
382
|
+
// the catch having emitted no terminal at all and reports `failed` alone. Emitted any
|
|
383
|
+
// earlier, a failing write would produce BOTH terminals and leave the fold's winner
|
|
384
|
+
// decided by arrival order. The sandbox teardown below emits nothing for the same reason.
|
|
385
|
+
await activity.complete();
|
|
329
386
|
}
|
|
330
387
|
finally {
|
|
331
388
|
try {
|
|
@@ -338,9 +395,14 @@ export async function runDataProfileBody(input, deps) {
|
|
|
338
395
|
}
|
|
339
396
|
catch (err) {
|
|
340
397
|
logger.error("profile failed", logger.errorFields(err));
|
|
341
|
-
|
|
398
|
+
const reason = profileFailureReason(err);
|
|
399
|
+
if (!unwrapOrThrow(await failDataProfile(deps.pool, analysisId, reason)))
|
|
342
400
|
logTerminalNoop(logger, analysisId, "failure");
|
|
343
401
|
await deps.runAuthorizer.revoke(authorization, "data-profile-failed");
|
|
402
|
+
// The same bounded, user-safe line the ledger receives — never the raw error, whose paths and
|
|
403
|
+
// stack frames stay in the log record above. This is the only other terminal emission, so
|
|
404
|
+
// reaching it means the success path did not emit one.
|
|
405
|
+
await activity.failed(reason);
|
|
344
406
|
}
|
|
345
407
|
}
|
|
346
408
|
/**
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { Pool } from "pg";
|
|
2
|
+
import { type AgentSession } from "../auth/types.js";
|
|
3
|
+
import type { ResourcePolicy, ResourceSpec } from "../config/resource-limits.js";
|
|
4
|
+
import type { Logger } from "../lib/logger.js";
|
|
5
|
+
import type { ChatProvider } from "../providers/types.js";
|
|
6
|
+
export declare const AD_HOC_ROUTER_AGENT_ID = "adhoc-router";
|
|
7
|
+
export declare const AD_HOC_ROUTER_TIMEOUT_MS = 10000;
|
|
8
|
+
export declare const AD_HOC_FALLBACK_AGENT_ID = "scientific-executor";
|
|
9
|
+
export interface AdHocRoute {
|
|
10
|
+
readonly agentId: string;
|
|
11
|
+
readonly resources: ResourceSpec;
|
|
12
|
+
readonly rationale: string;
|
|
13
|
+
readonly fallbackClass?: "timeout" | "provider_error" | "malformed" | "no_match" | "invalid_agent" | "invalid_resources";
|
|
14
|
+
}
|
|
15
|
+
export interface AdHocRouterDeps {
|
|
16
|
+
readonly provider: ChatProvider;
|
|
17
|
+
readonly model: string;
|
|
18
|
+
readonly pool: Pool;
|
|
19
|
+
readonly resourcePolicy?: ResourcePolicy;
|
|
20
|
+
readonly logger?: Logger;
|
|
21
|
+
/** Test seam; production always uses {@link AD_HOC_ROUTER_TIMEOUT_MS}. */
|
|
22
|
+
readonly timeoutMs?: number;
|
|
23
|
+
}
|
|
24
|
+
export declare function defaultAdHocResources(policy?: ResourcePolicy): ResourceSpec;
|
|
25
|
+
export declare function validAdHocResources(value: unknown, policy?: ResourcePolicy): ResourceSpec | null;
|
|
26
|
+
export declare function routeAdHocRequest(deps: AdHocRouterDeps, input: {
|
|
27
|
+
analysisId: string;
|
|
28
|
+
request: string;
|
|
29
|
+
session: AgentSession;
|
|
30
|
+
signal: AbortSignal;
|
|
31
|
+
}): Promise<AdHocRoute>;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import { jsonSchema, tool as aiTool } from "ai";
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
import { PLANNABLE_AGENT_CATALOG } from "../agents/sandbox-catalog.js";
|
|
4
|
+
import { forSubAgent } from "../auth/types.js";
|
|
5
|
+
import { DATA_PROFILE_ORIENTATION_MAX_CHARS, buildDataProfileOrientation } from "../app/data-profile-orientation.js";
|
|
6
|
+
import { createNoopLogger } from "../lib/console-logger.js";
|
|
7
|
+
import { unwrapOrThrow } from "../lib/result.js";
|
|
8
|
+
import { resourceEstimationSection } from "../prompts/planner.js";
|
|
9
|
+
import { loadDataProfileStatus } from "../state/index.js";
|
|
10
|
+
export const AD_HOC_ROUTER_AGENT_ID = "adhoc-router";
|
|
11
|
+
export const AD_HOC_ROUTER_TIMEOUT_MS = 10_000;
|
|
12
|
+
export const AD_HOC_FALLBACK_AGENT_ID = "scientific-executor";
|
|
13
|
+
const resourcesSchema = z.object({
|
|
14
|
+
cpu: z.number().positive(),
|
|
15
|
+
memoryGb: z.number().positive(),
|
|
16
|
+
gpu: z.object({ count: z.number().int().positive() }).optional(),
|
|
17
|
+
});
|
|
18
|
+
const routeSchema = z.object({
|
|
19
|
+
agentId: z.string().optional(),
|
|
20
|
+
resources: resourcesSchema.optional(),
|
|
21
|
+
rationale: z.string().optional(),
|
|
22
|
+
});
|
|
23
|
+
export function defaultAdHocResources(policy) {
|
|
24
|
+
return policy ? { cpu: Math.min(4, policy.perStep.maxCpu), memoryGb: Math.min(8, policy.perStep.maxMemoryGb) } : { cpu: 4, memoryGb: 8 };
|
|
25
|
+
}
|
|
26
|
+
export function validAdHocResources(value, policy) {
|
|
27
|
+
const parsed = resourcesSchema.safeParse(value);
|
|
28
|
+
if (!parsed.success)
|
|
29
|
+
return null;
|
|
30
|
+
const r = parsed.data;
|
|
31
|
+
const maxCpu = policy?.perStep.maxCpu ?? 4;
|
|
32
|
+
const maxMemoryGb = policy?.perStep.maxMemoryGb ?? 8;
|
|
33
|
+
const maxGpuCount = policy?.perStep.maxGpuCount ?? 0;
|
|
34
|
+
if (r.cpu > maxCpu || r.memoryGb > maxMemoryGb || (r.gpu?.count ?? 0) > maxGpuCount)
|
|
35
|
+
return null;
|
|
36
|
+
return r;
|
|
37
|
+
}
|
|
38
|
+
function resourceBounds(policy) {
|
|
39
|
+
const defaults = defaultAdHocResources(policy);
|
|
40
|
+
if (!policy) {
|
|
41
|
+
return `CPU range 1..${defaults.cpu}; default ${defaults.cpu}. Memory range 1..${defaults.memoryGb} GB; default ${defaults.memoryGb}. GPU range 0..0.`;
|
|
42
|
+
}
|
|
43
|
+
const { perStep } = policy;
|
|
44
|
+
return [
|
|
45
|
+
`CPU range ${Math.min(1, perStep.maxCpu)}..${perStep.maxCpu}; default ${defaults.cpu}.`,
|
|
46
|
+
`Memory range ${Math.min(1, perStep.maxMemoryGb)}..${perStep.maxMemoryGb} GB; default ${defaults.memoryGb}.`,
|
|
47
|
+
`GPU range 0..${perStep.maxGpuCount}; omit gpu unless required.`,
|
|
48
|
+
].join(" ");
|
|
49
|
+
}
|
|
50
|
+
async function profileOrientation(pool, analysisId, logger) {
|
|
51
|
+
try {
|
|
52
|
+
const status = unwrapOrThrow(await loadDataProfileStatus(pool, analysisId));
|
|
53
|
+
if (!status?.result)
|
|
54
|
+
return "No persisted data-profile facts are available.";
|
|
55
|
+
return buildDataProfileOrientation(status.result, DATA_PROFILE_ORIENTATION_MAX_CHARS);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
logger.warn("could not load data profile for ad hoc routing", { error: error instanceof Error ? error.message : String(error) });
|
|
59
|
+
return "The persisted data profile could not be loaded; route from the request alone.";
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
function routeTool() {
|
|
63
|
+
return {
|
|
64
|
+
submit_route: aiTool({
|
|
65
|
+
description: "Submit the single specialist and resource recommendation. This is the only valid response.",
|
|
66
|
+
inputSchema: jsonSchema(z.toJSONSchema(routeSchema)),
|
|
67
|
+
}),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
export async function routeAdHocRequest(deps, input) {
|
|
71
|
+
const logger = (deps.logger ?? createNoopLogger()).named("adhoc-router").with({ analysisId: input.analysisId, model: deps.model });
|
|
72
|
+
const catalog = PLANNABLE_AGENT_CATALOG.map((agent) => `- ${agent.id}: capabilities [${agent.capabilities.join(", ")}]; suitable for [${agent.suitableFor.join(", ")}]`).join("\n");
|
|
73
|
+
const orientation = await profileOrientation(deps.pool, input.analysisId, logger);
|
|
74
|
+
const controller = new AbortController();
|
|
75
|
+
const timer = setTimeout(() => controller.abort(new Error("ad hoc routing timed out")), deps.timeoutMs ?? AD_HOC_ROUTER_TIMEOUT_MS);
|
|
76
|
+
const signal = AbortSignal.any([input.signal, controller.signal]);
|
|
77
|
+
let raw;
|
|
78
|
+
let failure;
|
|
79
|
+
try {
|
|
80
|
+
const response = unwrapOrThrow(await deps.provider.chat({
|
|
81
|
+
system: [
|
|
82
|
+
"Select exactly one specialist for a targeted one-step analysis and estimate its sandbox resources.",
|
|
83
|
+
"Choose only from the supplied catalog. Do not create a plan, reject the request, or select scientific-executor.",
|
|
84
|
+
resourceEstimationSection(deps.resourcePolicy),
|
|
85
|
+
resourceBounds(deps.resourcePolicy),
|
|
86
|
+
"Respond only by calling submit_route.",
|
|
87
|
+
].join("\n\n"),
|
|
88
|
+
messages: [
|
|
89
|
+
{
|
|
90
|
+
role: "user",
|
|
91
|
+
content: `Request:\n${input.request}\n\nPersisted data-profile orientation:\n${orientation}\n\nEligible specialists:\n${catalog}`,
|
|
92
|
+
},
|
|
93
|
+
],
|
|
94
|
+
tools: routeTool(),
|
|
95
|
+
toolChoice: { type: "tool", toolName: "submit_route" },
|
|
96
|
+
}, forSubAgent(input.session, AD_HOC_ROUTER_AGENT_ID), signal));
|
|
97
|
+
const call = Array.isArray(response.message.content)
|
|
98
|
+
? response.message.content.find((part) => part.type === "tool-call" && part.toolName === "submit_route")
|
|
99
|
+
: undefined;
|
|
100
|
+
raw = call?.input;
|
|
101
|
+
if (!call)
|
|
102
|
+
failure = "malformed";
|
|
103
|
+
}
|
|
104
|
+
catch (error) {
|
|
105
|
+
if (input.signal.aborted)
|
|
106
|
+
throw error;
|
|
107
|
+
failure = controller.signal.aborted && !input.signal.aborted ? "timeout" : "provider_error";
|
|
108
|
+
logger.warn("ad hoc route fell back", { failure, error: error instanceof Error ? error.message : String(error) });
|
|
109
|
+
}
|
|
110
|
+
finally {
|
|
111
|
+
clearTimeout(timer);
|
|
112
|
+
}
|
|
113
|
+
const candidate = raw !== null && typeof raw === "object" ? raw : {};
|
|
114
|
+
const requestedAgent = typeof candidate.agentId === "string" ? candidate.agentId : undefined;
|
|
115
|
+
const validAgent = requestedAgent !== undefined && PLANNABLE_AGENT_CATALOG.some((agent) => agent.id === requestedAgent);
|
|
116
|
+
const agentId = validAgent ? requestedAgent : AD_HOC_FALLBACK_AGENT_ID;
|
|
117
|
+
if (!failure && !requestedAgent)
|
|
118
|
+
failure = "no_match";
|
|
119
|
+
if (!failure && !validAgent)
|
|
120
|
+
failure = "invalid_agent";
|
|
121
|
+
const validResources = validAdHocResources(candidate.resources, deps.resourcePolicy);
|
|
122
|
+
const resources = validResources ?? defaultAdHocResources(deps.resourcePolicy);
|
|
123
|
+
if (!validResources && candidate.resources !== undefined && !failure)
|
|
124
|
+
failure = "invalid_resources";
|
|
125
|
+
const rationale = typeof candidate.rationale === "string" && candidate.rationale.trim()
|
|
126
|
+
? candidate.rationale.trim()
|
|
127
|
+
: failure
|
|
128
|
+
? `Deterministic fallback: ${failure}`
|
|
129
|
+
: `Selected ${agentId}`;
|
|
130
|
+
logger.info("ad hoc route selected", { agentId, resources, rationale, failure });
|
|
131
|
+
return { agentId, resources, rationale, ...(failure ? { fallbackClass: failure } : {}) };
|
|
132
|
+
}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
/** Deterministic internal plan id in the existing persistence shape. */
|
|
2
|
+
export declare function adHocPlanId(analysisId: string, invocationId: string): string;
|
|
3
|
+
/** Deterministic RFC-4122 UUID used as both cortex run id and DBOS workflow id. */
|
|
4
|
+
export declare function adHocRunId(analysisId: string, invocationId: string): string;
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
const TOOL_ID = "execute_analysis";
|
|
3
|
+
function digest(analysisId, invocationId) {
|
|
4
|
+
return createHash("sha256").update(`${TOOL_ID}\0${analysisId}\0${invocationId}`, "utf8").digest();
|
|
5
|
+
}
|
|
6
|
+
/** Deterministic internal plan id in the existing persistence shape. */
|
|
7
|
+
export function adHocPlanId(analysisId, invocationId) {
|
|
8
|
+
return `pln-${digest(analysisId, invocationId).toString("hex", 0, 4)}`;
|
|
9
|
+
}
|
|
10
|
+
/** Deterministic RFC-4122 UUID used as both cortex run id and DBOS workflow id. */
|
|
11
|
+
export function adHocRunId(analysisId, invocationId) {
|
|
12
|
+
const bytes = Buffer.from(digest(analysisId, invocationId).subarray(4, 20));
|
|
13
|
+
bytes[6] = (bytes[6] & 0x0f) | 0x50;
|
|
14
|
+
bytes[8] = (bytes[8] & 0x3f) | 0x80;
|
|
15
|
+
const hex = bytes.toString("hex");
|
|
16
|
+
return `${hex.slice(0, 8)}-${hex.slice(8, 12)}-${hex.slice(12, 16)}-${hex.slice(16, 20)}-${hex.slice(20)}`;
|
|
17
|
+
}
|
|
@@ -28,7 +28,7 @@ export type ChemblOutput = {
|
|
|
28
28
|
readonly targets: ChemblTarget[];
|
|
29
29
|
};
|
|
30
30
|
export declare const chemblTool: import("../define-tool.js").Tool<{
|
|
31
|
-
action: "
|
|
31
|
+
action: "drug" | "targets" | "compounds" | "mechanism" | "bioactivity";
|
|
32
32
|
query?: string | undefined;
|
|
33
33
|
searchType?: "target" | "smiles" | "compound" | undefined;
|
|
34
34
|
chemblId?: string | undefined;
|
package/dist/tools/bio/chembl.js
CHANGED
|
@@ -18,74 +18,74 @@ import { ok } from "neverthrow";
|
|
|
18
18
|
import { z } from "zod";
|
|
19
19
|
import { defineTool } from "../define-tool.js";
|
|
20
20
|
import { getBioactivity, getDrugInfo, getMechanism, searchCompounds, searchTargets } from "../lib/chembl-client.js";
|
|
21
|
-
/**
|
|
21
|
+
/**
|
|
22
|
+
* Per-action record caps. The ceilings are what the endpoints will serve; the
|
|
23
|
+
* defaults are deliberately far below them — 500 activity rows is ~125k
|
|
24
|
+
* characters, which answers no question that the top 25 by potency does not.
|
|
25
|
+
*/
|
|
22
26
|
const LIMITS = {
|
|
23
27
|
/** `compounds` / `bioactivity` — activity-scale reads. */
|
|
24
|
-
|
|
28
|
+
wideMax: 500,
|
|
29
|
+
wideDefault: 25,
|
|
25
30
|
/** `drug` / `targets` — resolution reads, where a long list is noise. */
|
|
26
|
-
|
|
31
|
+
narrowMax: 25,
|
|
32
|
+
narrowDefault: 10,
|
|
27
33
|
};
|
|
28
34
|
const inputSchema = z
|
|
29
35
|
.object({
|
|
30
36
|
action: z
|
|
31
37
|
.enum(["compounds", "drug", "mechanism", "bioactivity", "targets"])
|
|
32
|
-
.describe("Which ChEMBL lookup to run
|
|
33
|
-
"'compounds' (query + searchType) — molecules by target, name
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"'
|
|
44
|
-
"
|
|
45
|
-
"
|
|
46
|
-
"'targets' (query) — resolve a gene symbol or protein name to a ChEMBL target: the step that produces the target ID " +
|
|
47
|
-
"used by bioactivity (idType='target') and compounds (searchType='target'). Returns targetChemblId, preferredName, " +
|
|
48
|
-
"targetType (SINGLE PROTEIN, PROTEIN COMPLEX, …), organism, geneNames. Results span organisms — check `organism` " +
|
|
49
|
-
"before using an ID, since the top hit for a human gene symbol may be a non-human ortholog."),
|
|
38
|
+
.describe("Which ChEMBL lookup to run; each names its params and return fields.\n" +
|
|
39
|
+
"'compounds' (query + searchType) — molecules by target, name or SMILES: resolve a named compound to its ID and structure, or list " +
|
|
40
|
+
"what was assayed against a target. → chemblId, preferredCompoundName, canonicalSmiles, molecularWeight, alogp, molecularFormula.\n" +
|
|
41
|
+
"'drug' (query) — the drug registry by drug name or indication: 'what treats X?', 'is Y approved, since when?'. → moleculeChemblId, " +
|
|
42
|
+
"preferredName, maxPhase (4 = approved), moleculeType, firstApproval, indication. If the drug endpoint is empty it falls back to a " +
|
|
43
|
+
"max_phase >= 4 molecule search, whose rows carry indication: null.\n" +
|
|
44
|
+
"'mechanism' (chemblId, molecule only) — curated mechanism of ONE molecule: 'how does X work?'. → mechanismOfAction, actionType " +
|
|
45
|
+
"(INHIBITOR, AGONIST, …), targetChemblId + targetName, moleculeChemblId. Curated mainly for clinical/approved molecules, so tool " +
|
|
46
|
+
"compounds often have none.\n" +
|
|
47
|
+
"'bioactivity' (chemblId + idType) — measured activity rows: the curated, QUOTABLE potency. → standardType, standardValue + " +
|
|
48
|
+
"standardUnits, pchemblValue (normalized -log10), assayChemblId, assayType, compoundChemblId, targetChemblId.\n" +
|
|
49
|
+
"'targets' (query) — resolve a gene symbol or protein name to a ChEMBL target, producing the ID that bioactivity (idType='target') " +
|
|
50
|
+
"and compounds (searchType='target') need. → targetChemblId, preferredName, targetType, organism, geneNames. Check `organism`: the " +
|
|
51
|
+
"top hit for a human symbol may be a non-human ortholog."),
|
|
50
52
|
query: z
|
|
51
53
|
.string()
|
|
52
54
|
.min(1)
|
|
53
55
|
.optional()
|
|
54
|
-
.describe("Required for 'compounds', 'drug', 'targets'. compounds: must match searchType — a target name/gene symbol or target " +
|
|
55
|
-
"
|
|
56
|
-
"
|
|
56
|
+
.describe("Required for 'compounds', 'drug', 'targets'. compounds: must match searchType — a target name/gene symbol or target ChEMBL ID, a " +
|
|
57
|
+
"compound name, or a SMILES. drug: a drug name ('imatinib') or an indication ('melanoma'). targets: a gene symbol ('EGFR'), a " +
|
|
58
|
+
"protein name, or a ChEMBL target ID."),
|
|
57
59
|
searchType: z
|
|
58
60
|
.enum(["target", "compound", "smiles"])
|
|
59
61
|
.optional()
|
|
60
|
-
.describe("Required for 'compounds' — how to read `query`. 'target': resolve
|
|
61
|
-
"
|
|
62
|
-
"structure search on canonical SMILES."),
|
|
62
|
+
.describe("Required for 'compounds' — how to read `query`. 'target': resolve to a ChEMBL target, then return what was assayed against it. " +
|
|
63
|
+
"'compound': free-text over molecule names. 'smiles': flexmatch structure search."),
|
|
63
64
|
chemblId: z
|
|
64
65
|
.string()
|
|
65
66
|
.min(1)
|
|
66
67
|
.optional()
|
|
67
|
-
.describe("Required for 'mechanism' and 'bioactivity'. A
|
|
68
|
-
"
|
|
68
|
+
.describe("Required for 'mechanism' and 'bioactivity'. A molecule ID ('CHEMBL25' = aspirin), or — for bioactivity with idType='target' — a target " +
|
|
69
|
+
"ID ('CHEMBL203' = EGFR). 'mechanism' takes a molecule ID only."),
|
|
69
70
|
idType: z
|
|
70
71
|
.enum(["compound", "target"])
|
|
71
72
|
.optional()
|
|
72
|
-
.describe("Required for 'bioactivity' — which side of the activity table `chemblId` indexes. 'compound':
|
|
73
|
-
"
|
|
74
|
-
"assayed against that target."),
|
|
73
|
+
.describe("Required for 'bioactivity' — which side of the activity table `chemblId` indexes. 'compound': everything that molecule was assayed " +
|
|
74
|
+
"against. 'target': every compound assayed against that target."),
|
|
75
75
|
activityType: z
|
|
76
76
|
.string()
|
|
77
77
|
.optional()
|
|
78
|
-
.describe("'bioactivity' only. Exact
|
|
79
|
-
"(case-sensitive); omit to get all activity types."),
|
|
78
|
+
.describe("'bioactivity' only. Exact, case-sensitive standard_type filter — 'IC50', 'EC50', 'Ki', 'Kd'. Omit for all types."),
|
|
80
79
|
limit: z
|
|
81
80
|
.number()
|
|
82
81
|
.int()
|
|
83
82
|
.min(1)
|
|
84
|
-
.max(LIMITS.
|
|
83
|
+
.max(LIMITS.wideMax)
|
|
85
84
|
.optional()
|
|
86
|
-
.describe(`Max records. 'compounds'/'bioactivity': 1–${LIMITS.
|
|
87
|
-
`(default ${LIMITS.
|
|
88
|
-
`
|
|
85
|
+
.describe(`Max records. 'compounds'/'bioactivity': 1–${LIMITS.wideMax} (default ${LIMITS.wideDefault}). 'drug'/'targets': 1–${LIMITS.narrowMax} ` +
|
|
86
|
+
`(default ${LIMITS.narrowDefault}). Ignored by 'mechanism'. Rows are UNSORTED, so raising this is the only way to widen the window — ` +
|
|
87
|
+
`do it to survey an SAR series or a full assay panel, not to browse. With searchType='target' it caps the activity rows scanned, so ` +
|
|
88
|
+
`fewer unique compounds come back.`),
|
|
89
89
|
})
|
|
90
90
|
.refine((d) => !(d.action === "compounds" || d.action === "drug" || d.action === "targets") || (d.query !== undefined && d.query.length > 0), {
|
|
91
91
|
message: "query is required when action is 'compounds', 'drug', or 'targets'",
|
|
@@ -104,35 +104,32 @@ const inputSchema = z
|
|
|
104
104
|
message: "idType is required when action is 'bioactivity' — 'compound' if chemblId is a molecule ID, 'target' if it is a target ID",
|
|
105
105
|
path: ["idType"],
|
|
106
106
|
})
|
|
107
|
-
.refine((d) => !(d.action === "drug" || d.action === "targets") || d.limit === undefined || d.limit <= LIMITS.
|
|
108
|
-
message: `limit is capped at ${LIMITS.
|
|
107
|
+
.refine((d) => !(d.action === "drug" || d.action === "targets") || d.limit === undefined || d.limit <= LIMITS.narrowMax, {
|
|
108
|
+
message: `limit is capped at ${LIMITS.narrowMax} for action 'drug' and 'targets' (only 'compounds' and 'bioactivity' reach ${LIMITS.wideMax})`,
|
|
109
109
|
path: ["limit"],
|
|
110
110
|
});
|
|
111
111
|
export const chemblTool = defineTool({
|
|
112
112
|
id: "chembl",
|
|
113
|
-
description: "ChEMBL — the manually curated database of drug-like bioactives (~2.4M compounds), the targets they were measured " +
|
|
114
|
-
"
|
|
115
|
-
"
|
|
116
|
-
"
|
|
117
|
-
"
|
|
118
|
-
"
|
|
119
|
-
"
|
|
120
|
-
"compounds here first. If ChEMBL does not find the compound, resolve it in PubChem with pubchem action='compound' and bridge back " +
|
|
121
|
-
"to a ChEMBL ID via pubchem action='crossrefs'.\n" +
|
|
122
|
-
"An empty array is a valid 'no match' / no-data answer, not an error — do not retry the same call.",
|
|
113
|
+
description: "ChEMBL — the manually curated database of drug-like bioactives (~2.4M compounds), the targets they were measured against, their mechanisms and " +
|
|
114
|
+
"their approval status. Five lookups; pick with `action`, which gives each one's params and return fields.\n" +
|
|
115
|
+
"IDs are resolved, never guessed: a `chemblId` comes from a prior action='compounds'/'drug' (molecules) or action='targets' (targets), or from " +
|
|
116
|
+
"pubchem action='crossrefs'. A compound or gene name is not a ChEMBL ID.\n" +
|
|
117
|
+
"Being curated, ChEMBL is what you quote from: prefer action='bioactivity' over pubchem action='assays' for any number you will cite, and resolve " +
|
|
118
|
+
"compounds here first. If ChEMBL misses the compound, resolve it via pubchem action='compound' and bridge back with pubchem action='crossrefs'.\n" +
|
|
119
|
+
"An empty array is valid no-data, not an error — do not retry the same call.",
|
|
123
120
|
inputSchema,
|
|
124
121
|
execute: async ({ action, query, searchType, chemblId, idType, activityType, limit }) => {
|
|
125
122
|
switch (action) {
|
|
126
123
|
case "compounds":
|
|
127
|
-
return ok({ compounds: await searchCompounds(query, searchType, limit ?? LIMITS.
|
|
124
|
+
return ok({ compounds: await searchCompounds(query, searchType, limit ?? LIMITS.wideDefault) });
|
|
128
125
|
case "drug":
|
|
129
|
-
return ok({ drugs: await getDrugInfo(query, limit ?? LIMITS.
|
|
126
|
+
return ok({ drugs: await getDrugInfo(query, limit ?? LIMITS.narrowDefault) });
|
|
130
127
|
case "mechanism":
|
|
131
128
|
return ok({ mechanisms: await getMechanism(chemblId) });
|
|
132
129
|
case "bioactivity":
|
|
133
|
-
return ok({ activities: await getBioactivity(chemblId, idType, { activityType, limit: limit ?? LIMITS.
|
|
130
|
+
return ok({ activities: await getBioactivity(chemblId, idType, { activityType, limit: limit ?? LIMITS.wideDefault }) });
|
|
134
131
|
case "targets":
|
|
135
|
-
return ok({ targets: await searchTargets(query, limit ?? LIMITS.
|
|
132
|
+
return ok({ targets: await searchTargets(query, limit ?? LIMITS.narrowDefault) });
|
|
136
133
|
}
|
|
137
134
|
},
|
|
138
135
|
});
|