Skip to content
Open
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
36 changes: 33 additions & 3 deletions agents/nucliadb/src/hyperforge_nucliadb_agentic/agent.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
from hyperforge.configure import agent
from hyperforge.context.agent import ContextAgent
from hyperforge.definition import FunctionDefinition
from hyperforge.llm_config import LLMConfig
from hyperforge.manager import Manager, build_reasoning
from hyperforge.memory import Chunk, Context, QuestionMemory, Source
from hyperforge.models import JSONObject
Expand Down Expand Up @@ -67,16 +68,29 @@ async def choose_sources(
question: str,
ident: str,
step_title: str,
model: LLMConfig | None = None,
) -> list[Source]:
if len(sources) == 1:
return [Source.model_construct(id=sources[0])]
kbid = None
if model is None and sources:
kbids = {get_ndb_driver(manager, source).config.kbid for source in sources}
if len(kbids) != 1:
raise ValueError(
"An explicit routing model is required for sources from different KBs"
)
kbid = kbids.pop()
if not kbid or not kbid.strip():
raise ValueError("A KB ID is required to inherit its generative model")
return await choose_source(
memory,
manager,
sources,
question,
ident=ident,
step_title=step_title,
choose_source_model=model,
kbid=kbid,
)


Expand Down Expand Up @@ -775,6 +789,7 @@ async def ask_agent(
question,
ident=self.agent_id,
step_title=self.step_title("Choose sources"),
model=self.config.generative_model,
)

contexts: list[Context] = await asyncio.gather(
Expand Down Expand Up @@ -812,6 +827,7 @@ async def _get_question_context(
question,
ident=self.agent_id,
step_title=self.step_title("Choose sources"),
model=self.config.generative_model,
)

missing: list[tuple[str, str] | None] = await asyncio.gather(
Expand Down Expand Up @@ -979,6 +995,8 @@ async def prepare_ask_request(
rag_strategies: list[RagStrategies],
) -> AskRequest:
"""Apply agent defaults, search config, public ASK overrides, and runtime query."""
if not nucliadb_driver.config.kbid or not nucliadb_driver.config.kbid.strip():
raise ValueError("A KB ID is required for KB source generation")
# Load explicit options from the public ASK request.
ask_request: AskRequest | None = None
if ask_request_json:
Expand Down Expand Up @@ -1007,14 +1025,15 @@ async def prepare_ask_request(
)

# Agent defaults fill any remaining fields.
fallback_values = {
fallback_values: dict[str, object] = {
"show": [ResourceProperties.BASIC, ResourceProperties.ORIGIN],
"citations": CitationsType.LLM_FOOTNOTES,
"generative_model": self.config.generative_model.model_id,
"rag_strategies": rag_strategies,
"generate_answer": self.config.generate_inner_answer,
"reasoning": build_reasoning(self.config.generative_model),
}
if self.config.generative_model is not None:
fallback_values["generative_model"] = self.config.generative_model.model_id
fallback_values["reasoning"] = build_reasoning(self.config.generative_model)
return ask_request.model_copy(
update={
field: value
Expand Down Expand Up @@ -1072,6 +1091,8 @@ async def inner_rag(
memory.arguments.get("ask_request"),
rag_strategies,
)
if ask_request.generative_model is None:
manager.require_kb_model_scope(nucliadb_driver.config.kbid)

filter_expression = await self.build_filter_expression(
nucliadb_driver,
Expand Down Expand Up @@ -1233,6 +1254,7 @@ async def facets_search(
question,
ident=self.agent_id,
step_title=self.step_title("Choose sources"),
model=self.config.generative_model,
)
contexts: list[Context] = await asyncio.gather(
*[
Expand Down Expand Up @@ -1287,6 +1309,7 @@ async def inner_facets_search(
"additionalProperties": False,
},
model=self.config.generative_model,
kbid=ndb.config.kbid,
tracking=memory.get_tracking_info(),
)
labelsets_selected = selected_labels.get("label_sets", [])
Expand Down Expand Up @@ -1341,6 +1364,7 @@ async def inner_facets_search(
prompt,
user_id="rao-facets-answer-generation",
model=self.config.generative_model,
kbid=ndb.config.kbid,
tracking=memory.get_tracking_info(),
)
# TODO: Error if code is not success
Expand Down Expand Up @@ -1392,6 +1416,7 @@ async def facets_count(
question,
ident=self.agent_id,
step_title=self.step_title("Choose sources"),
model=self.config.generative_model,
)
contexts: list[Context] = await asyncio.gather(
*[
Expand Down Expand Up @@ -1441,6 +1466,7 @@ async def inner_facets(
"additionalProperties": False,
},
model=self.config.generative_model,
kbid=ndb.config.kbid,
tracking=memory.get_tracking_info(),
)
labels_selected = selected_labels.get("label_sets", [])
Expand All @@ -1462,6 +1488,7 @@ async def inner_facets(
prompt,
user_id="rao-facets-answer-generation",
model=self.config.generative_model,
kbid=ndb.config.kbid,
tracking=memory.get_tracking_info(),
)
# TODO: Error if code is not success
Expand Down Expand Up @@ -1512,6 +1539,7 @@ async def catalog_search(
question,
ident=self.agent_id,
step_title=self.step_title("Choose sources"),
model=self.config.generative_model,
)
contexts: list[Context] = await asyncio.gather(
*[
Expand Down Expand Up @@ -1557,6 +1585,7 @@ async def inner_catalog_search(
"required": ["filters"],
},
model=self.config.generative_model,
kbid=ndb.config.kbid,
tracking=memory.get_tracking_info(),
)
filters = self.parse_selected_filters(question, source, selected_filters)
Expand Down Expand Up @@ -1587,6 +1616,7 @@ async def inner_catalog_search(
prompt,
user_id="rao-catalog-search-answer",
model=self.config.generative_model,
kbid=ndb.config.kbid,
tracking=memory.get_tracking_info(),
)
# TODO: Error if code is not success
Expand Down
8 changes: 4 additions & 4 deletions agents/nucliadb/src/hyperforge_nucliadb_agentic/config.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from typing import List, Literal, Optional, Tuple

from hyperforge.context.agent import ContextAgentConfig
from hyperforge.llm_config import LLMConfig, LLMField, llm_defaults
from hyperforge.llm_config import LLMField
from hyperforge.utils import WidgetType
from pydantic import Field
from pydantic.config import ConfigDict
Expand All @@ -20,10 +20,10 @@ class NucliaDBAgentConfig(ContextAgentConfig):
"show_in_node": True,
},
)
generative_model: LLMField = Field(
default=LLMConfig(model_id=llm_defaults.default),
generative_model: LLMField | None = Field(
default=None,
title="Generative model",
description="Model used to generate answers",
description="Model used by KB source tools. If unset, inherit the Knowledge Box's configured model.",
)
published_functions: Optional[Tuple[str, ...]] = Field(
default=(
Expand Down
Loading
Loading