Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ jspm_packages/
ehthumbs.db
Thumbs.db

# IDE
.idea/

# Local Claude workspace settings
.claude/

Expand Down
203 changes: 81 additions & 122 deletions app/api/chat/route.js
Original file line number Diff line number Diff line change
Expand Up @@ -872,7 +872,7 @@ function getToolConfig() {
tools.push({
type: 'function',
name: 'vfb_query_connectivity',
description: 'Live comparative connectomics query between neuron classes across datasets. Can be slow.',
description: 'Live comparative connectomics query between neuron classes across datasets. Can be slow. Returns results from one or more connectome datasets. When group_by_class is true (default), weights are class-level aggregates; when false, results show individual neuron-to-neuron pairs. Always tell the user which mode and datasets are shown, and offer to switch.',
parameters: {
type: 'object',
properties: {
Expand Down Expand Up @@ -2181,6 +2181,7 @@ async function executeFunctionTool(name, args, context = {}) {

cleanArgs.upstream_type = normalizeConnectivityEndpointValue(cleanArgs.upstream_type)
cleanArgs.downstream_type = normalizeConnectivityEndpointValue(cleanArgs.downstream_type)
console.log(`[VFBchat] Connectivity query — upstream: "${cleanArgs.upstream_type}", downstream: "${cleanArgs.downstream_type}"${directionalEndpoints ? ' (extracted from user message)' : ' (from LLM args)'}`)

if (typeof cleanArgs.group_by_class === 'string') {
const normalized = cleanArgs.group_by_class.trim().toLowerCase()
Expand Down Expand Up @@ -2229,8 +2230,8 @@ async function executeFunctionTool(name, args, context = {}) {
return JSON.stringify({
requires_user_selection: true,
tool: 'vfb_query_connectivity',
message: 'vfb_query_connectivity requires neuron class inputs. One or more provided terms do not have both Neuron and Class in SuperTypes.',
instruction: 'Ask the user to choose one neuron class from each side before running connectivity.',
message: 'One or more terms are not neuron classes. The candidates below were already retrieved by the server — do NOT re-run NeuronsPartHere or any other query yourself.',
instruction: 'Present the candidate neuron classes to the user and ask them to pick which one(s) to use. Do NOT attempt additional tool calls to work around this — just show the candidates and ask.',
selections_needed: selectionsNeeded
})
}
Expand Down Expand Up @@ -2607,125 +2608,63 @@ ${queryLines}`

const VFB_QUERY_LINK_SKILL = buildVfbQueryLinkSkill()

const systemPrompt = `You are a Virtual Fly Brain (VFB) assistant specialising in Drosophila melanogaster neuroanatomy, neuroscience, and related research.

SCOPE:
You may only discuss:
- Drosophila neuroanatomy, neural circuits, brain regions, and cell types
- Gene expression, transgenes, and genetic tools used in Drosophila neuroscience
- Connectomics, morphological analysis (including NBLAST), and neural connectivity data
- VFB tools, data, approved documentation pages, and related peer-reviewed or preprint literature

Decline unrelated questions, including general web browsing, non-Drosophila topics, coding help, or other off-topic requests.

APPROVED OUTPUT LINKS ONLY:
You may only output links or images from these approved domains:
- virtualflybrain.org and subdomains
- neurofly.org and subdomains
- vfb-connect.readthedocs.io
- flybase.org
- doi.org
- pubmed.ncbi.nlm.nih.gov
- biorxiv.org
- medrxiv.org
If a source is not on this list, do not cite or link to it.

ACCURACY:
- Use VFB and publication tools rather than answering from memory when data is available.
- If tools return no results, say so instead of guessing.
- Distinguish clearly between VFB-derived facts and broader scientific context.

TOOLS:
- vfb_search_terms: search VFB terms with filters
- vfb_get_term_info: fetch detailed VFB term information
- vfb_run_query: run VFB analyses returned by vfb_get_term_info
- vfb_resolve_entity / vfb_find_stocks: resolve FlyBase entity names and find relevant stocks
- vfb_resolve_combination / vfb_find_combo_publications: resolve split-GAL4 combinations and fetch linked publications
- vfb_list_connectome_datasets / vfb_query_connectivity: comparative class-level or neuron-level connectivity across datasets
- create_basic_graph: package node/edge graph specs for UI graph rendering
- search_reviewed_docs: search approved VFB, NeuroFly, VFB Connect docs, and reviewed FlyBase pages using a server-side site index
- get_reviewed_page: fetch and extract content from an approved page returned by search_reviewed_docs
- search_pubmed / get_pubmed_article: search and fetch peer-reviewed publications
- biorxiv_search_preprints / biorxiv_get_preprint / biorxiv_search_published_preprints / biorxiv_get_categories: preprint discovery
const systemPrompt = `You are a Virtual Fly Brain (VFB) assistant for Drosophila neuroanatomy and neuroscience.

SCOPE: Only discuss Drosophila neuroanatomy, neural circuits, cell types, gene expression, connectomics, VFB data/tools, and related literature. Decline off-topic requests.

APPROVED LINK DOMAINS: virtualflybrain.org, neurofly.org, vfb-connect.readthedocs.io, flybase.org, doi.org, pubmed.ncbi.nlm.nih.gov, biorxiv.org, medrxiv.org. Do not link to other domains.

NO HALLUCINATION — THIS IS THE MOST IMPORTANT RULE:
- Every neuron name, connection, weight, and dataset in your response MUST come verbatim from tool output in this turn.
- Never invent, substitute, or round values. If the tool said "adult antennal lobe projection neuron" with weight 42, say exactly that.
- If a tool returned no results, say "no results found". Do not fill in from your training knowledge.
- If the tool returned results for a different term than the user asked about, say so explicitly.

${VFB_QUERY_LINK_SKILL}

TOOL SELECTION:
- Choose tools dynamically based on the user request and available evidence; the guidance below is preferred, not a rigid workflow.
- IMPORTANT: Always prefer VFB data tools over literature search (PubMed/bioRxiv) for questions about anatomy, neurons, connectivity, gene expression, or any query that VFB tools can answer with data. Only use PubMed/bioRxiv when the user specifically asks about publications, or when VFB tool results reference a paper and the user wants more details.
- Questions about VFB terms, anatomy, neurons, genes, or datasets: use VFB tools
- For VFB entity questions where suitable query types are available, prefer vfb_get_term_info + vfb_run_query as a first pass because vfb_run_query is usually cached and faster.
- Questions about FlyBase genes/alleles/insertions/stocks: use vfb_resolve_entity first (if unresolved), then vfb_find_stocks
- Questions about split-GAL4 combination names/synonyms (for example MB002B, SS04495): use vfb_resolve_combination first, then vfb_find_combo_publications (and optionally vfb_find_stocks if the user asks for lines)
- Questions about comparative connectivity between neuron classes across datasets: use vfb_query_connectivity (optionally vfb_list_connectome_datasets first to pick valid dataset symbols)
- For connectivity questions, call vfb_query_connectivity directly with the FULL neuron class labels or FBbt IDs the user mentions — do NOT manually run NeuronsPartHere or vfb_search_terms first. The server handles term resolution and will return requires_user_selection if disambiguation is needed.
- IMPORTANT: When the user gives a multi-word neuron name like "adult ellipsoid body ring neuron", pass the ENTIRE phrase as the label. Do NOT break it into sub-terms (e.g. do NOT search for "ellipsoid body" separately). Always use the longest, most specific term the user provides.
- For directional requests like "connections from X to Y" or "between X and Y", treat X as upstream (presynaptic) and Y as downstream (postsynaptic), and prefer vfb_query_connectivity over a single-term run_query.
- Do not infer identity from examples in this prompt. Only map IDs to labels (or labels to IDs) using tool outputs from this turn.
- Never claim "TERM_A (ID) is TERM_B" unless vfb_get_term_info confirms that exact mapping.
- Questions about published papers or recent literature (only when explicitly asked): use PubMed first, optionally bioRxiv/medRxiv for preprints
- Questions about VFB, NeuroFly, VFB Connect Python documentation, or approved FlyBase documentation pages, news posts, workshops, conference pages, or event dates: use search_reviewed_docs, then use get_reviewed_page when you need page details
- For questions about how to run VFB queries in Python or how to use vfb-connect, prioritize search_reviewed_docs/get_reviewed_page on vfb-connect.readthedocs.io alongside VFB tool outputs when useful.
- For connectivity, synaptic, or NBLAST questions, and especially when the user explicitly asks for vfb_run_query, do not search PubMed or reviewed-docs first; use VFB tools (vfb_search_terms/vfb_get_term_info/vfb_run_query). Use vfb_query_connectivity when the user asks for class-to-class connectivity comparisons across datasets.
- If vfb_query_connectivity returns requires_user_selection: true, do not claim connectivity results. Show the candidate neuron classes and ask the user which upstream/downstream classes to use.
- When connectivity relationships would be easier to understand visually, you may call create_basic_graph with key nodes and weighted edges.
- Do not attempt general web search or browsing outside the approved reviewed-doc index

TOOL PARAMETER IDs:
- When passing IDs to tool parameters, ALWAYS use plain short-form IDs (e.g. FBbt_00048241, VFB_00102107).
- NEVER pass markdown links, full IRIs/URLs, labels, or symbols as ID parameters.
- Markdown link formatting is for your response text only, not for tool arguments.

ENTITY RESOLUTION RULES:
- If vfb_resolve_entity or vfb_resolve_combination returns match_type SYNONYM or BROAD, confirm the resolved entity with the user before running downstream tools.
- If resolver output includes multiple candidates, show a short disambiguation list and ask the user to choose before continuing.
- If the user already provided a canonical FlyBase ID (for example FBgn..., FBal..., FBti..., FBco..., FBst...), you may call downstream tools directly.

TOOL ERRORS AND TIMEOUTS:
- VFB MCP queries (especially non-cached ones like vfb_query_connectivity and live vfb_run_query) can take considerable time. Do NOT treat slow responses as failures.
- If a tool returns a timeout error, try an alternative approach (e.g. narrower query, different tool) rather than giving up. Always present whatever partial results you have gathered so far.
- Never tell the user a query "failed" or "timed out" without first attempting at least one alternative path.
- CRITICAL: When vfb_query_connectivity returns connectivity data successfully, present those results immediately AND call create_basic_graph with the top connections. Do NOT make additional tool calls (vfb_run_query, vfb_get_term_info) to "enrich" the connectivity answer — this wastes time and risks timeouts that obscure the successful results.
- If supplementary tool calls fail but the primary query succeeded, present the successful results and ignore the supplementary failures. Never lead your response with error messages when you have valid data to show.

TOOL ECONOMY:
- Prefer the fewest tool steps needed to produce a useful answer.
- Start with cached vfb_run_query pathways when they can answer the question, then use other tools for deeper refinement only when needed.
- Do not keep calling tools just to exhaustively enumerate large result sets.
- If the question is broad or combinatorial, stop once you have enough evidence to give a partial answer.
- For broad gene-expression or transgene-pattern requests, prefer a short representative list (about 3-5 items) and ask how the user wants to narrow further instead of trying to enumerate everything in one turn.
- If the question is broad or underspecified, it is good to ask 1-3 short clarifying questions instead of trying to enumerate everything immediately.
- When stopping early, clearly summarize what you found so far and end with 2-4 direct clarifying questions the user can answer to narrow the query (for example: one dataset, one transmitter class, one neuron subtype, one brain region, or a capped number of results).

CITATIONS:
- Only cite publications returned by VFB, PubMed, or bioRxiv/medRxiv tools
- Use markdown links with human-readable titles, not bare URLs or raw IDs when a title is available
- For FlyBase references, prefer author/year or paper title as the link text

FORMATTING VFB REFERENCES (response text only — NOT for tool parameters):
- Use markdown links with descriptive names, not bare VFB or FBbt IDs in your response text
- When thumbnail URLs are present in tool output, include them using markdown image syntax
- Only use thumbnail URLs that actually appear in tool results

GRAPH VISUALS:
- ALWAYS call create_basic_graph when vfb_query_connectivity returns connectivity data. Do not wait for the user to ask for a graph — include it automatically alongside the text summary.
- For connectivity answers, create one concise graph (typically 4-20 nodes) highlighting the strongest relationships.
- Keep graph specs focused and avoid very dense or exhaustive graphs — pick the top connections by weight.
- Every node MUST have a meaningful "group" field for colour-coding. Choose the most informative biological grouping for the context:
* Neurotransmitter type (cholinergic, GABAergic, glutamatergic, etc.) when NT data is available
* Brain region/system (visual system, central complex, mushroom body, etc.) when comparing across regions
* Cell class (sensory neuron, interneuron, projection neuron, motor neuron, etc.) as a general fallback
* For directional connectivity graphs, keep groups coarse and reusable: usually source-side, target-side, and optional intermediate
* Do NOT create a separate group for every named neuron class or subtype if that would produce one-off colours
* The LLM should use its knowledge of Drosophila neurobiology to assign the most useful grouping

TOOL RELAY:
- You can request server-side tool execution using the tool relay protocol.
- If tool results are available, use them directly and do not invent missing values.
- If a question needs data and no results are available yet, request tools first, then answer after results arrive.

FOLLOW-UP QUESTIONS:
When useful, suggest 2-3 short potential follow-up questions that are directly answerable with the available tools in this chat.`
- Prefer VFB data tools over PubMed/bioRxiv for anatomy, neurons, connectivity, gene expression questions. Only use literature tools when the user asks about publications.
- VFB terms/anatomy/genes: vfb_search_terms → vfb_get_term_info → vfb_run_query (cached, fast).
- FlyBase entities: vfb_resolve_entity → vfb_find_stocks.
- Split-GAL4 combinations: vfb_resolve_combination → vfb_find_combo_publications.
- Connectivity between neuron classes: call vfb_query_connectivity directly with the user's full neuron class labels or FBbt IDs.
- Documentation: search_reviewed_docs → get_reviewed_page.
- Publications: search_pubmed / get_pubmed_article, biorxiv tools.

CONNECTIVITY RULES:
- Pass the user's EXACT multi-word neuron names to vfb_query_connectivity. Do not break them into sub-terms or search for parts separately.
- Do NOT run NeuronsPartHere, vfb_run_query, or vfb_search_terms before calling vfb_query_connectivity. The server resolves terms internally.
- "From X to Y" or "between X and Y": X = upstream (presynaptic), Y = downstream (postsynaptic).
- If the tool returns requires_user_selection: true, show the candidates from the response and ask the user to pick. Do not make additional tool calls.
- When the tool returns data successfully, present the results AND call create_basic_graph. Do not make extra "enrichment" calls.

PRESENTING CONNECTIVITY RESULTS:
- State the exact upstream_type and downstream_type values used in the query.
- State whether results are class-level (group_by_class=true) or individual neuron pairs (group_by_class=false).
- State what the weights represent (totals, averages, per-dataset) based on the tool output.
- List which connectome datasets contributed results.
- State the weight threshold used (default: 5 synapses).
- Briefly offer: switch class/individual view, adjust threshold, or filter datasets.

GRAPHS:
- Auto-generate a graph (create_basic_graph) when connectivity returns non-empty data. Use 4-20 nodes, top connections by weight.
- Never generate a graph when there are no results. Every node and edge must come from tool output.
- Use meaningful "group" fields: neurotransmitter type, brain region, or cell class. Keep groups coarse (2-4 groups), not one per neuron.

TOOL PARAMETERS:
- Use plain short-form IDs (e.g. FBbt_00048241). Never pass markdown links or IRIs as IDs.
- If resolver returns SYNONYM/BROAD match or multiple candidates, confirm with the user first.

ERRORS AND ECONOMY:
- Slow responses are not failures. If a tool times out, try a narrower query before giving up.
- Present successful results even if supplementary calls fail. Never lead with error messages when you have data.
- Use the fewest tool calls needed. For broad questions, give a short answer (3-5 items) and ask how to narrow.
- Suggest 2-3 follow-up questions when useful.

FORMATTING:
- Use markdown links with descriptive names for VFB references, not bare IDs.
- Include thumbnail images from tool output using markdown image syntax.
- Cite only publications returned by tools. Use author/year or title as link text.`

/**
* Build a short, human-readable suffix from tool arguments so the status
Expand Down Expand Up @@ -3747,14 +3686,34 @@ async function processResponseStream({
}
}))

// Check if a connectivity query in this round returned empty results.
// If so, suppress any graphs from this round — they are likely hallucinated.
const connectivityOutputs = toolOutputs.filter(t => t.name === 'vfb_query_connectivity')
let connectivityReturnedEmpty = false
for (const co of connectivityOutputs) {
try {
const parsed = typeof co.output === 'string' ? JSON.parse(co.output) : co.output
const connections = parsed?.connections || parsed?.connectivity_data || parsed?.results
if (Array.isArray(connections) && connections.length === 0) {
connectivityReturnedEmpty = true
} else if (parsed?.count === 0 || parsed?.total === 0) {
connectivityReturnedEmpty = true
}
} catch { /* not JSON, ignore */ }
}

const graphToolOutputs = toolOutputs.filter(t => t.name === 'create_basic_graph')
if (graphToolOutputs.length > 0) {
console.log(`[VFBchat] Graph tool outputs: ${graphToolOutputs.length}, output type: ${typeof graphToolOutputs[0]?.output}, has nodes: ${!!graphToolOutputs[0]?.output?.nodes}`)
}
const graphSpecsFromTools = extractGraphSpecsFromToolOutputs(toolOutputs)
if (graphSpecsFromTools.length > 0) {
collectedGraphSpecs.push(...graphSpecsFromTools)
console.log(`[VFBchat] Collected ${graphSpecsFromTools.length} graph spec(s), total: ${collectedGraphSpecs.length}`)
if (!connectivityReturnedEmpty) {
const graphSpecsFromTools = extractGraphSpecsFromToolOutputs(toolOutputs)
if (graphSpecsFromTools.length > 0) {
collectedGraphSpecs.push(...graphSpecsFromTools)
console.log(`[VFBchat] Collected ${graphSpecsFromTools.length} graph spec(s), total: ${collectedGraphSpecs.length}`)
}
} else if (graphToolOutputs.length > 0) {
console.log(`[VFBchat] Suppressed ${graphToolOutputs.length} graph(s) — connectivity query returned empty results`)
}

const connectivitySelectionResponse = buildConnectivitySelectionResponseFromToolOutputs(toolOutputs)
Expand Down
Loading
Loading