nucliadb-agentic-api 1.0.0.post226__tar.gz → 1.0.0.post240__tar.gz

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.
Files changed (46) hide show
  1. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/PKG-INFO +1 -1
  2. nucliadb_agentic_api-1.0.0.post240/VERSION +1 -0
  3. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/pyproject.toml +1 -1
  4. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/pyproject.toml.orig +1 -1
  5. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/db/transform.py +90 -88
  6. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/v1/ask_websocket.py +19 -0
  7. nucliadb_agentic_api-1.0.0.post226/VERSION +0 -1
  8. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/README.md +0 -0
  9. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/alembic/README +0 -0
  10. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/alembic/env.py +0 -0
  11. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/alembic/script.py.mako +0 -0
  12. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/alembic/versions/7f3b2f92a8c1_add_agentic_config_table.py +0 -0
  13. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/alembic/versions/b3c4d5e6f7a8_add_sources_table.py +0 -0
  14. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/alembic/versions/c4d5e6f7a8b9_add_agentic_config_soft_deletion.py +0 -0
  15. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/alembic.ini +0 -0
  16. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/VERSION +0 -0
  17. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/__init__.py +0 -0
  18. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/agentic/__init__.py +0 -0
  19. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/agentic/ask_handler.py +0 -0
  20. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/agentic/ask_result.py +0 -0
  21. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/agentic/ask_transform_to_interaction.py +0 -0
  22. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/app.py +0 -0
  23. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/commands.py +0 -0
  24. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/db/__init__.py +0 -0
  25. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/db/agentic_configs.py +0 -0
  26. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/db/settings.py +0 -0
  27. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/db/sources.py +0 -0
  28. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/exceptions.py +0 -0
  29. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/logging.py +0 -0
  30. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/models.py +0 -0
  31. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/py.typed +0 -0
  32. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/server/__init__.py +0 -0
  33. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/server/server.py +0 -0
  34. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/server/session.py +0 -0
  35. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/server/settings.py +0 -0
  36. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/settings.py +0 -0
  37. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/utils.py +0 -0
  38. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/v1/__init__.py +0 -0
  39. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/v1/agentic_configs.py +0 -0
  40. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/v1/ask.py +0 -0
  41. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/v1/mcp_content.py +0 -0
  42. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/v1/mcp_nucliadb.py +0 -0
  43. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/v1/oauth.py +0 -0
  44. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/v1/router.py +0 -0
  45. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/v1/sources.py +0 -0
  46. {nucliadb_agentic_api-1.0.0.post226 → nucliadb_agentic_api-1.0.0.post240}/src/nucliadb_agentic_api/v1/utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: nucliadb_agentic_api
3
- Version: 1.0.0.post226
3
+ Version: 1.0.0.post240
4
4
  Summary: NucliaDB Agentic API for Hyperforge
5
5
  Author: AI Data Team
6
6
  Author-email: AI Data Team <learning@nuclia.com>
@@ -0,0 +1 @@
1
+ 1.0.0.post240
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "nucliadb_agentic_api"
7
- version = "1.0.0.post226"
7
+ version = "1.0.0.post240"
8
8
  description = "NucliaDB Agentic API for Hyperforge"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.12"
@@ -4,7 +4,7 @@ build-backend = "uv_build"
4
4
 
5
5
  [project]
6
6
  name = "nucliadb_agentic_api"
7
- version = "1.0.0.post226"
7
+ version = "1.0.0.post240"
8
8
  authors = [{ name = "AI Data Team", email = "learning@nuclia.com" }]
9
9
  description = "NucliaDB Agentic API for Hyperforge"
10
10
  readme = "README.md"
@@ -1,11 +1,14 @@
1
- from typing import Any, Dict, Tuple
1
+ from typing import Dict, Tuple
2
2
  from uuid import uuid4
3
3
 
4
+ from hyperforge.agent import AgentConfig
5
+ from hyperforge.context.config import ContextAgentConfig
4
6
  from hyperforge.driver import DriverConfig
5
7
  from hyperforge.models import MemoryConfig, Rules
6
8
  from hyperforge.retrieval.config import RetrievalAgentConfig
7
9
  from hyperforge.workflows import WorkflowData
8
- from hyperforge_mcp.config import Transport
10
+ from hyperforge_google.config import GoogleAgentConfig
11
+ from hyperforge_mcp.config import MCPAgentConfig, Transport
9
12
  from hyperforge_mcp.config_driver import (
10
13
  MCPHTTPDriverConfig,
11
14
  MCPHTTPInnerConfig,
@@ -14,10 +17,15 @@ from hyperforge_nucliadb.driver_config import (
14
17
  NucliaDBConfig,
15
18
  NucliaDBConnection,
16
19
  )
20
+ from hyperforge_nucliadb_agentic.config import NucliaDBAgentConfig
17
21
  from hyperforge_nucliadb_agentic.internal_driver import (
18
22
  InternalNucliaDBConfig,
19
23
  InternalNucliaDBConnection,
20
24
  )
25
+ from hyperforge_perplexity.config import PerplexityAgentConfig
26
+ from hyperforge_rephrase.config import RephraseAgentConfig
27
+ from hyperforge_smart.config import SmartAgentConfig
28
+ from hyperforge_summarize.config import SummarizeAgentConfig
21
29
 
22
30
  from nucliadb_agentic_api.db.sources import Sources
23
31
  from nucliadb_agentic_api.models import AgenticConfigSchema
@@ -43,60 +51,35 @@ async def transform_agentic_config(
43
51
  global_driver = []
44
52
 
45
53
  title = agentic_config.title if agentic_config.title else "Default Agentic Config"
54
+ preprocess: list[AgentConfig] = []
55
+ context: list[AgentConfig] = []
56
+ generation: list[AgentConfig] = []
46
57
 
47
58
  if agentic_config.rephrase:
48
- config: Dict[str, Any] = {
49
- "title": f"{title} - Retrieval",
50
- }
51
- if agentic_config.rephrase.model:
52
- config["model"] = agentic_config.rephrase.model
53
- if agentic_config.rephrase.prompt:
54
- config["rules"] = [agentic_config.rephrase.prompt]
55
- if agentic_config.rephrase.ask_to:
56
- # Same KB with different layers
57
- config["kbid"] = agentic_config.rephrase.ask_to
58
- if agentic_config.rephrase.history is not None:
59
- config["history"] = agentic_config.rephrase.history
60
-
61
- config["module"] = "rephrase"
62
- preprocess = [config]
63
- else:
64
- preprocess = []
59
+ rephrase = agentic_config.rephrase
60
+ rephrase_config = RephraseAgentConfig(
61
+ title=f"{title} - Retrieval",
62
+ rules=[rephrase.prompt] if rephrase.prompt else None,
63
+ kb=rephrase.ask_to,
64
+ history=rephrase.history if rephrase.history is not None else False,
65
+ )
66
+ if rephrase.model:
67
+ rephrase_config.model = rephrase.model
68
+ preprocess.append(rephrase_config)
65
69
 
66
70
  if agentic_config.smart_agent:
67
- config = {
68
- "title": f"{title} - Smart Agent",
69
- }
70
- if agentic_config.smart_agent.extra_prompt:
71
- config["extra_prompt"] = agentic_config.smart_agent.extra_prompt
72
- if agentic_config.smart_agent.models:
73
- if agentic_config.smart_agent.models.context_validation:
74
- config["context_validation_model"] = (
75
- agentic_config.smart_agent.models.context_validation
76
- )
77
- if agentic_config.smart_agent.models.planner:
78
- config["planner_model"] = agentic_config.smart_agent.models.planner
79
- if agentic_config.smart_agent.models.executor:
80
- config["executor_model"] = agentic_config.smart_agent.models.executor
81
- if agentic_config.smart_agent.extra_prompt:
82
- config["extra_prompt"] = agentic_config.smart_agent.extra_prompt
83
- if agentic_config.smart_agent.history is not None:
84
- config["history"] = agentic_config.smart_agent.history
85
- registered_agents = []
86
- for source in agentic_config.smart_agent.sources:
71
+ smart_agent = agentic_config.smart_agent
72
+ registered_agents: list[ContextAgentConfig] = []
73
+ for source in smart_agent.sources:
87
74
  source_obj = await source_manager.get_source(account, kbid, source)
88
- source_config: Dict[str, Any] = {
89
- "title": f"{title} - Smart Agent - {source_obj.type.capitalize()} Source",
90
- }
75
+ source_title = (
76
+ f"{title} - Smart Agent - {source_obj.type.capitalize()} Source"
77
+ )
91
78
 
92
79
  if source_obj.type == "nucliadb":
93
80
  # Same KB different
94
81
  uid = uuid4().hex
95
82
 
96
- source_config["id"] = f"nucliadb-agent-{source}"
97
- source_config["sources"] = [uid]
98
- source_config["module"] = "nucliadb_agent"
99
-
100
83
  ndb_driver_config: DriverConfig
101
84
  if internal_nucliadb and internal_nucliadb_url:
102
85
  ndb_driver_config = InternalNucliaDBConfig(
@@ -137,15 +120,13 @@ async def transform_agentic_config(
137
120
  "No NucliaDB URL configured for internal or external access"
138
121
  )
139
122
  drivers[uid] = ndb_driver_config
140
- registered_agents.append(source_config)
123
+ registered_agents.append(
124
+ NucliaDBAgentConfig(
125
+ id=f"nucliadb-agent-{source}", title=source_title, sources=[uid]
126
+ )
127
+ )
141
128
  elif source_obj.type == "mcp":
142
129
  uid = uuid4().hex
143
- # See the nucliadb branch above for why this must be
144
- # deterministic rather than random.
145
- source_config["id"] = f"mcp-agent-{source}"
146
- source_config["sources"] = [uid]
147
- source_config["transport"] = Transport.HTTP
148
- source_config["module"] = "mcp"
149
130
  mcp_driver_config = MCPHTTPDriverConfig(
150
131
  identifier=uid,
151
132
  name="MCPHTTP",
@@ -156,47 +137,68 @@ async def transform_agentic_config(
156
137
  ),
157
138
  ) # TODO: pass real config if needed
158
139
  drivers[uid] = mcp_driver_config
159
- registered_agents.append(source_config)
140
+ registered_agents.append(
141
+ MCPAgentConfig(
142
+ id=f"mcp-agent-{source}",
143
+ title=source_title,
144
+ source=uid,
145
+ transport=Transport.HTTP,
146
+ tool_choice_model=source_obj.tool_choice_model or "chatgpt-4.1",
147
+ valid_headers=source_obj.valid_headers or [],
148
+ )
149
+ )
160
150
 
161
151
  elif source_obj.type == "google":
162
- source_config["id"] = f"google-agent-{source}"
163
- source_config["module"] = "google"
164
152
  global_driver.append("google")
165
- registered_agents.append(source_config)
153
+ registered_agents.append(
154
+ GoogleAgentConfig(id=f"google-agent-{source}", title=source_title)
155
+ )
166
156
 
167
157
  elif source_obj.type == "perplexity":
168
- source_config["id"] = f"perplexity-agent-{source}"
169
- source_config["module"] = "perplexity"
170
158
  global_driver.append("perplexity")
171
- registered_agents.append(source_config)
159
+ registered_agents.append(
160
+ PerplexityAgentConfig(
161
+ id=f"perplexity-agent-{source}",
162
+ title=source_title,
163
+ domain=source_obj.enabled_domains or [],
164
+ prompt=None,
165
+ )
166
+ )
172
167
 
173
- config["registered_agents"] = registered_agents
174
- config["module"] = "smart"
175
- context = [config]
176
- else:
177
- context = []
168
+ models = smart_agent.models
169
+ smart_config = SmartAgentConfig(
170
+ title=f"{title} - Smart Agent",
171
+ planning_mode=smart_agent.mode.value,
172
+ extra_prompt=smart_agent.extra_prompt,
173
+ history=smart_agent.history if smart_agent.history is not None else False,
174
+ # SmartAgentConfig's before-validator accepts serialized agent configs.
175
+ registered_agents=[agent.model_dump() for agent in registered_agents], # type: ignore[misc]
176
+ )
177
+ # Only set the models if they are provided; otherwise, leave them as None to use hyperforge's default models.
178
+ # TODO: Provide nucliadb agentic api level defaults for models
179
+ if models:
180
+ if models.context_validation:
181
+ smart_config.context_validation_model = models.context_validation
182
+ if models.planner:
183
+ smart_config.planner_model = models.planner
184
+ if models.executor:
185
+ smart_config.executor_model = models.executor
186
+ context.append(smart_config)
178
187
 
179
188
  if agentic_config.summarize:
180
- config = {
181
- "title": f"{title} - Summarize Agent",
182
- }
183
- if agentic_config.summarize.model:
184
- config["model"] = agentic_config.summarize.model
185
- if agentic_config.summarize.user_prompt:
186
- config["user_prompt"] = agentic_config.summarize.user_prompt
187
- if agentic_config.summarize.system_prompt:
188
- config["system_prompt"] = agentic_config.summarize.system_prompt
189
- config["conversational"] = agentic_config.summarize.conversational
190
- config["citations"] = True
191
- config["force_chunk_level_citations"] = False
192
- if agentic_config.summarize.history is not None:
193
- config["history"] = agentic_config.summarize.history
194
-
195
- config["module"] = "summarize"
196
-
197
- generation = [config]
198
- else:
199
- generation = []
189
+ summarize = agentic_config.summarize
190
+ summarize_config = SummarizeAgentConfig(
191
+ title=f"{title} - Summarize Agent",
192
+ prompt=summarize.user_prompt,
193
+ system_prompt=summarize.system_prompt,
194
+ conversational=summarize.conversational,
195
+ citations=True,
196
+ force_chunk_level_citations=False,
197
+ history=summarize.history if summarize.history is not None else False,
198
+ )
199
+ if summarize.model:
200
+ summarize_config.model = summarize.model
201
+ generation.append(summarize_config)
200
202
 
201
203
  return (
202
204
  RetrievalAgentConfig(
@@ -209,9 +211,9 @@ async def transform_agentic_config(
209
211
  description="Default workflow for agentic config transformation",
210
212
  parameters=None,
211
213
  ),
212
- preprocess=preprocess, # type: ignore
213
- context=context, # type: ignore
214
- generation=generation, # type: ignore
214
+ preprocess=preprocess,
215
+ context=context,
216
+ generation=generation,
215
217
  postprocess=[],
216
218
  ),
217
219
  drivers,
@@ -5,6 +5,7 @@ from fastapi import Header, Query, WebSocket, WebSocketDisconnect
5
5
  from hyperforge.api.authentication import requires
6
6
  from hyperforge.api.v1.interaction import WebsocketReceiver, stream_response
7
7
  from hyperforge.interaction import AnswerOperation, AragAnswer, ARAGException
8
+ from hyperforge_nucliadb_agentic.ask.audit import get_audit, get_trace_id
8
9
  from hyperforge_nucliadb_agentic.ask.model import (
9
10
  AskRequest,
10
11
  )
@@ -21,6 +22,7 @@ from nucliadb_models.security import RequestSecurity
21
22
  from nucliadb_utils.authentication import NucliaUser
22
23
  from pydantic import ValidationError
23
24
 
25
+ from nucliadb_agentic_api import logger
24
26
  from nucliadb_agentic_api.v1.router import router
25
27
 
26
28
 
@@ -127,6 +129,23 @@ async def websocket_endpoint(
127
129
  interaction,
128
130
  workflow_id=agentic_config_id,
129
131
  ):
132
+ if msg.step and msg.step.external_usage:
133
+ audit = get_audit()
134
+ if audit is not None:
135
+ audit.report_step_usage(
136
+ account_id=x_stf_account,
137
+ kbid=kbid,
138
+ client_type=x_ndb_client,
139
+ step=msg.step,
140
+ trace_id=get_trace_id(),
141
+ )
142
+ else:
143
+ logger.warning(
144
+ "Skipping WebSocket external usage report because audit utility is unavailable account=%s kb=%s module=%s",
145
+ x_stf_account,
146
+ kbid,
147
+ msg.step.module,
148
+ )
130
149
  try:
131
150
  await websocket.send_text(msg.model_dump_json())
132
151
  except (RuntimeError, WebSocketDisconnect):
@@ -1 +0,0 @@
1
- 1.0.0.post226