nucliadb-agentic-api 1.0.0.post156__py3-none-any.whl

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 (37) hide show
  1. nucliadb_agentic_api/VERSION +1 -0
  2. nucliadb_agentic_api/__init__.py +20 -0
  3. nucliadb_agentic_api/agentic/__init__.py +0 -0
  4. nucliadb_agentic_api/agentic/ask_handler.py +66 -0
  5. nucliadb_agentic_api/agentic/ask_result.py +389 -0
  6. nucliadb_agentic_api/agentic/ask_transform_to_interaction.py +10 -0
  7. nucliadb_agentic_api/app.py +147 -0
  8. nucliadb_agentic_api/commands.py +66 -0
  9. nucliadb_agentic_api/db/__init__.py +0 -0
  10. nucliadb_agentic_api/db/agentic_configs.py +296 -0
  11. nucliadb_agentic_api/db/settings.py +11 -0
  12. nucliadb_agentic_api/db/sources.py +202 -0
  13. nucliadb_agentic_api/db/transform.py +208 -0
  14. nucliadb_agentic_api/exceptions.py +10 -0
  15. nucliadb_agentic_api/logging.py +18 -0
  16. nucliadb_agentic_api/models.py +321 -0
  17. nucliadb_agentic_api/py.typed +0 -0
  18. nucliadb_agentic_api/server/__init__.py +1 -0
  19. nucliadb_agentic_api/server/server.py +113 -0
  20. nucliadb_agentic_api/server/session.py +276 -0
  21. nucliadb_agentic_api/server/settings.py +22 -0
  22. nucliadb_agentic_api/settings.py +39 -0
  23. nucliadb_agentic_api/utils.py +284 -0
  24. nucliadb_agentic_api/v1/__init__.py +19 -0
  25. nucliadb_agentic_api/v1/agentic_configs.py +117 -0
  26. nucliadb_agentic_api/v1/ask.py +303 -0
  27. nucliadb_agentic_api/v1/ask_websocket.py +141 -0
  28. nucliadb_agentic_api/v1/mcp_content.py +310 -0
  29. nucliadb_agentic_api/v1/mcp_nucliadb.py +785 -0
  30. nucliadb_agentic_api/v1/oauth.py +60 -0
  31. nucliadb_agentic_api/v1/router.py +3 -0
  32. nucliadb_agentic_api/v1/sources.py +158 -0
  33. nucliadb_agentic_api/v1/utils.py +12 -0
  34. nucliadb_agentic_api-1.0.0.post156.dist-info/METADATA +150 -0
  35. nucliadb_agentic_api-1.0.0.post156.dist-info/RECORD +37 -0
  36. nucliadb_agentic_api-1.0.0.post156.dist-info/WHEEL +4 -0
  37. nucliadb_agentic_api-1.0.0.post156.dist-info/entry_points.txt +6 -0
@@ -0,0 +1,60 @@
1
+ import logging
2
+
3
+ from fastapi import Query
4
+ from starlette.requests import Request
5
+ from starlette.responses import HTMLResponse
6
+
7
+ from nucliadb_agentic_api.settings import Settings
8
+ from nucliadb_agentic_api.v1.router import router
9
+ from nucliadb_agentic_api.v1.utils import tracer
10
+
11
+ logger = logging.getLogger(__name__)
12
+
13
+ RENDER = "<html><body><h1>OAuth Completed</h1><p>You can close this window and return to the application.</p></body></html>"
14
+
15
+
16
+ @router.get(
17
+ "/api/auth/kb/{kbid}/workflow/{workflow_id}/session/{session}/oauth/{oauth_uuid}/callback",
18
+ status_code=200,
19
+ description="OAuth callback endpoint for retrieval agent workflows. This endpoint is called by the RAO after the user completes the OAuth flow, and it sends the obtained credentials to the corresponding websocket.",
20
+ tags=["Retrieval Agent"],
21
+ include_in_schema=False,
22
+ )
23
+ async def oauth_callback(
24
+ request: Request,
25
+ kbid: str,
26
+ session: str,
27
+ workflow_id: str,
28
+ oauth_uuid: str,
29
+ question_id: str = Query(..., include_in_schema=False),
30
+ state: str = Query(..., include_in_schema=False),
31
+ account_id: str = Query(..., include_in_schema=False),
32
+ ):
33
+ """
34
+ Callback from oauth flow on RAO that requires to send creds to websocket
35
+ """
36
+ settings: Settings = request.app.settings
37
+ subject = settings.oauth_subject.format(
38
+ account=account_id,
39
+ agent_id=kbid,
40
+ session=session,
41
+ question=question_id,
42
+ oauth_uuid=oauth_uuid,
43
+ workflow_id=workflow_id,
44
+ )
45
+ # Request a question
46
+ with tracer().start_as_current_span("Request activation"):
47
+ logger.info(
48
+ "OAuth callback received for agent=%s, session=%s, oauth_uuid=%s, question_id=%s",
49
+ kbid,
50
+ session,
51
+ oauth_uuid,
52
+ question_id,
53
+ )
54
+ await request.app.broker.send_reply(subject, state)
55
+ logger.info(
56
+ "OAuth callback published to stream %s",
57
+ subject,
58
+ )
59
+
60
+ return HTMLResponse(content=RENDER)
@@ -0,0 +1,3 @@
1
+ from fastapi.routing import APIRouter
2
+
3
+ router = APIRouter()
@@ -0,0 +1,158 @@
1
+ from typing import TYPE_CHECKING, Union, cast
2
+
3
+ from fastapi import Header, HTTPException, Request, Response
4
+ from nucliadb_models.resource import NucliaDBRoles
5
+ from nucliadb_utils.authentication import requires
6
+
7
+ from nucliadb_agentic_api import exceptions
8
+ from nucliadb_agentic_api.models import (
9
+ GoogleSourceSchema,
10
+ MCPSourceSchema,
11
+ NucliaDBSourceSchema,
12
+ PerplexitySourceSchema,
13
+ )
14
+ from nucliadb_agentic_api.v1.router import router
15
+
16
+ if TYPE_CHECKING:
17
+ from nucliadb_agentic_api.app import HTTPApplication
18
+
19
+ # FastAPI / OpenAPI needs a concrete type for response_model — use the Union directly.
20
+ _AnySource = Union[
21
+ NucliaDBSourceSchema,
22
+ PerplexitySourceSchema,
23
+ MCPSourceSchema,
24
+ GoogleSourceSchema,
25
+ ]
26
+
27
+
28
+ @router.post(
29
+ "/api/v1/kb/{kbid}/sources/{source_id}",
30
+ status_code=201,
31
+ summary="Create a source",
32
+ tags=["Sources"],
33
+ )
34
+ @requires(NucliaDBRoles.OWNER)
35
+ async def create_source_endpoint(
36
+ request: Request,
37
+ kbid: str,
38
+ source_id: str,
39
+ item: _AnySource,
40
+ x_nucliadb_account: str = Header(default="", include_in_schema=False),
41
+ ) -> Response:
42
+ app = cast("HTTPApplication", request.app)
43
+ try:
44
+ await app.source_manager.create_source(
45
+ account=x_nucliadb_account,
46
+ kbid=kbid,
47
+ source_id=source_id,
48
+ source=item,
49
+ )
50
+ except exceptions.Conflict as exc:
51
+ raise HTTPException(status_code=409, detail=str(exc)) from exc
52
+ return Response(status_code=201)
53
+
54
+
55
+ @router.get(
56
+ "/api/v1/kb/{kbid}/sources/{source_id}",
57
+ status_code=200,
58
+ summary="Get a source",
59
+ tags=["Sources"],
60
+ response_model=_AnySource,
61
+ response_model_exclude_none=True,
62
+ )
63
+ @requires(NucliaDBRoles.READER)
64
+ async def get_source_endpoint(
65
+ request: Request,
66
+ kbid: str,
67
+ source_id: str,
68
+ x_nucliadb_account: str = Header(default="", include_in_schema=False),
69
+ ) -> _AnySource:
70
+ app = cast("HTTPApplication", request.app)
71
+ try:
72
+ return await app.source_manager.get_source( # type: ignore[return-value]
73
+ account=x_nucliadb_account,
74
+ kbid=kbid,
75
+ source_id=source_id,
76
+ )
77
+ except exceptions.NotFound as exc:
78
+ raise HTTPException(status_code=404, detail=str(exc)) from exc
79
+
80
+
81
+ @router.get(
82
+ "/api/v1/kb/{kbid}/sources",
83
+ status_code=200,
84
+ summary="List sources",
85
+ tags=["Sources"],
86
+ response_model=dict[str, _AnySource],
87
+ response_model_exclude_none=True,
88
+ )
89
+ @requires(NucliaDBRoles.READER)
90
+ async def list_sources_endpoint(
91
+ request: Request,
92
+ kbid: str,
93
+ x_nucliadb_account: str = Header(default="", include_in_schema=False),
94
+ ) -> dict[str, _AnySource]:
95
+ app = cast("HTTPApplication", request.app)
96
+ return await app.source_manager.list_sources( # type: ignore[return-value]
97
+ account=x_nucliadb_account,
98
+ kbid=kbid,
99
+ )
100
+
101
+
102
+ @router.patch(
103
+ "/api/v1/kb/{kbid}/sources/{source_id}",
104
+ status_code=204,
105
+ summary="Update a source",
106
+ tags=["Sources"],
107
+ )
108
+ @requires(NucliaDBRoles.OWNER)
109
+ async def patch_source_endpoint(
110
+ request: Request,
111
+ kbid: str,
112
+ source_id: str,
113
+ item: _AnySource,
114
+ x_nucliadb_account: str = Header(default="", include_in_schema=False),
115
+ ) -> Response:
116
+ app = cast("HTTPApplication", request.app)
117
+ try:
118
+ await app.source_manager.patch_source(
119
+ account=x_nucliadb_account,
120
+ kbid=kbid,
121
+ source_id=source_id,
122
+ source=item,
123
+ )
124
+ except exceptions.NotFound as exc:
125
+ raise HTTPException(status_code=404, detail=str(exc)) from exc
126
+ return Response(status_code=204)
127
+
128
+
129
+ @router.delete(
130
+ "/api/v1/kb/{kbid}/sources/{source_id}",
131
+ status_code=204,
132
+ summary="Delete a source",
133
+ tags=["Sources"],
134
+ )
135
+ @requires(NucliaDBRoles.OWNER)
136
+ async def delete_source_endpoint(
137
+ request: Request,
138
+ kbid: str,
139
+ source_id: str,
140
+ x_nucliadb_account: str = Header(default="", include_in_schema=False),
141
+ ) -> Response:
142
+ app = cast("HTTPApplication", request.app)
143
+ try:
144
+ await app.source_manager.delete_source(
145
+ account=x_nucliadb_account,
146
+ kbid=kbid,
147
+ source_id=source_id,
148
+ )
149
+ except exceptions.NotFound as exc:
150
+ raise HTTPException(status_code=404, detail=str(exc)) from exc
151
+
152
+ # Cascade: remove every agentic config that references this source.
153
+ await app.agent_manager.delete_configs_referencing_source(
154
+ account=x_nucliadb_account,
155
+ kbid=kbid,
156
+ source_id=source_id,
157
+ )
158
+ return Response(status_code=204)
@@ -0,0 +1,12 @@
1
+ from nucliadb_telemetry.utils import get_telemetry
2
+ from opentelemetry import trace
3
+
4
+ from nucliadb_agentic_api import SERVICE_NAME
5
+
6
+
7
+ def tracer():
8
+ provider = get_telemetry(SERVICE_NAME)
9
+ if provider:
10
+ return provider.get_tracer(__name__)
11
+ else:
12
+ return trace.NoOpTracer()
@@ -0,0 +1,150 @@
1
+ Metadata-Version: 2.4
2
+ Name: nucliadb_agentic_api
3
+ Version: 1.0.0.post156
4
+ Summary: NucliaDB Agentic API for Hyperforge
5
+ Author: AI Data Team
6
+ Author-email: AI Data Team <learning@nuclia.com>
7
+ License-Expression: Apache-2.0
8
+ Classifier: Programming Language :: Python :: 3
9
+ Classifier: Operating System :: OS Independent
10
+ Requires-Python: >=3.10
11
+ Project-URL: Progress, https://progress.com
12
+ Project-URL: Github, https://github.com/nuclia/forge
13
+ Project-URL: API Reference, https://docs.rag.progress.cloud
14
+ Description-Content-Type: text/markdown
15
+
16
+ # NucliaDB Agentic API
17
+
18
+ The NucliaDB Agentic API package exposes NucliaDB-oriented agentic capabilities
19
+ for Hyperforge, including ASK/search flows and MCP integrations.
20
+
21
+ ## Install
22
+
23
+ From the workspace root:
24
+
25
+ ```bash
26
+ uv sync
27
+ ```
28
+
29
+ ## Run
30
+
31
+ Start the service:
32
+
33
+ ```bash
34
+ uv run nucliadb-agentic-api
35
+ ```
36
+
37
+ Useful endpoints:
38
+
39
+ - `/health/ready`
40
+ - `/health/alive`
41
+ - `/metrics`
42
+
43
+ ## Configuration
44
+
45
+ Runtime configuration is provided through environment variables consumed by
46
+ Pydantic settings. Common settings include:
47
+
48
+ - `HTTP_HOST` and `HTTP_PORT`
49
+ - `MEMORY_READER_NUCLIADB`, `MEMORY_WRITER_NUCLIADB`, and
50
+ `MEMORY_SEARCH_NUCLIADB`
51
+ - `MEMORY_APIKEY_NUCLIADB`
52
+ - `VALKEY_URL`
53
+ - `IDP_REGIONAL_GRPC`
54
+ - `LOAD_MODULES`
55
+
56
+ ## Development
57
+
58
+ Run the package tests from the workspace root:
59
+
60
+ ```bash
61
+ make test
62
+ ```
63
+
64
+
65
+ # Objective
66
+ Enhance the RAG experience by offering RAO features directly in a KB.
67
+
68
+ # How?
69
+ The same way we store search configs in the KB, we could store agentic configs, and when calling the `/ask` endpoint we can refer to a given agentic config.
70
+
71
+ # Scope
72
+
73
+ The corresponding RAO workflow will be:
74
+ - a Rephrase (optionally)
75
+ - a SmartAgent
76
+ - a Summarize
77
+
78
+ Possible sources for the SmartAgent:
79
+ - The current KB (possibly several times with different filters)
80
+ - Sync service (using connections defined in the current KB)
81
+ - Google
82
+ - Perplexity
83
+ - MCP
84
+
85
+ Out of scope
86
+ - Other KBs
87
+ - SQL
88
+ - Snowflake (?)
89
+ - Sitefinity (?)
90
+
91
+ # Config spec
92
+
93
+ ```json
94
+ {
95
+ "rephrase": { // optional
96
+ "ask_to": <filter_expression>, //optional
97
+ "prompt": <string>, // optional
98
+ "model": <model>, // optional
99
+ },
100
+ "smart_agent": {
101
+ "mode": <reactive | plan_execute>,
102
+ "extra_prompt": <string>, // optional
103
+ "models": { // optional
104
+ "context_validation": <model>, // optional
105
+ "planner": <model>, // optional
106
+ "executor": <model>, // optional
107
+ },
108
+ "sources": [<source list>]
109
+ },
110
+ "summarize": { // optional
111
+ "user_prompt": <string>, // optional
112
+ "system_prompt": <string>, // optional
113
+ "conversational": <boolean>,
114
+ "model": <model>, // optional
115
+ // and citations must be forced to chunk-level
116
+ }
117
+ }
118
+ ```
119
+
120
+ And sources can be:
121
+
122
+ ```json
123
+ {
124
+ "type": "nucliadb",
125
+ "description": <string>,
126
+ "filter_expression": <filter_expression>, //optional
127
+ }
128
+
129
+ {
130
+ "type": "sync",
131
+ "description": <string>,
132
+ "connection": <connection_id>
133
+ }
134
+
135
+ {
136
+ "type": "google",
137
+ "description": <string>
138
+ }
139
+
140
+ {
141
+ "type": "perplexity",
142
+ "description": <string>
143
+ }
144
+
145
+ {
146
+ "type": "mcp",
147
+ "description": <string>,
148
+ <...the MCP driver params>
149
+ }
150
+ ````
@@ -0,0 +1,37 @@
1
+ nucliadb_agentic_api/VERSION,sha256=klIfw8vZZL3J9YSpkbif3apXVO0cyW1tQkRTOGacEwU,5
2
+ nucliadb_agentic_api/__init__.py,sha256=Dd3wVAa4_n_DgffcbDgYGGwOJG4df2Ut5TZsziMVlLM,535
3
+ nucliadb_agentic_api/agentic/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
+ nucliadb_agentic_api/agentic/ask_handler.py,sha256=aDqqnKZKYfDMstWJ71dTCWpxD53N1A7tqH4Nsqj9jNY,1792
5
+ nucliadb_agentic_api/agentic/ask_result.py,sha256=heffxPzUpFnv2J8J2NCiSnDyw0VRURCXUHGrIdqVs5k,18301
6
+ nucliadb_agentic_api/agentic/ask_transform_to_interaction.py,sha256=K58uO5-avpN6KfY1H9rqkNFOm7Z6fnhaS8wg8lPCK70,533
7
+ nucliadb_agentic_api/app.py,sha256=WexI5Wb_gpiguHKqWjTUJxbX5OQXJDvKlEM0eb5Rmnw,4758
8
+ nucliadb_agentic_api/commands.py,sha256=437hJmA14qk9aHmerrUJZ6WwtsVNPKsxUfPc3MhfCK0,2332
9
+ nucliadb_agentic_api/db/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
10
+ nucliadb_agentic_api/db/agentic_configs.py,sha256=A9fiokCeyOR5L00lFtUu6rtsXsfx0evziFAODVUFpd0,11162
11
+ nucliadb_agentic_api/db/settings.py,sha256=isEpkWqzVT4Ve8SHGTKhkbGt875z9Hvu4WQz8j5Zf2k,348
12
+ nucliadb_agentic_api/db/sources.py,sha256=XjoD-OeocbxQnbI5qFgALS2Z8lYw9p9nbi8lE0ZDeg0,6575
13
+ nucliadb_agentic_api/db/transform.py,sha256=cuo8O2HJyVj1a308thRK4B8XiXBAvoNDZBn0xa5OP1M,8361
14
+ nucliadb_agentic_api/exceptions.py,sha256=TL5N61QO5nY-IvwNp2U1d1e7SnGAcjNL_LKA5jkkcSM,277
15
+ nucliadb_agentic_api/logging.py,sha256=7SopZTmibSynt9s5z6ZhFBrP03kxkW4gvPPq93mxdvs,594
16
+ nucliadb_agentic_api/models.py,sha256=hckn26KRDXsRgSaCwzoWT7IRNKTswmMfz7OrjYNkrfY,8334
17
+ nucliadb_agentic_api/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
18
+ nucliadb_agentic_api/server/__init__.py,sha256=8TQPCRw8LryJtqViqZbWIoQPsegSZx3N01LLmsMdCPk,45
19
+ nucliadb_agentic_api/server/server.py,sha256=7qULDLgCiaKhzXzON2yplvrlOo7b3e20aLEBeOO76KU,3803
20
+ nucliadb_agentic_api/server/session.py,sha256=TgCAzAfaHurPxB-W3sBK3TXbh-0PAxJdklodhbKXiKw,9482
21
+ nucliadb_agentic_api/server/settings.py,sha256=82ovLv9X35ckreONqiOLAG5uWcmJMbQQXX1oB_xghM0,726
22
+ nucliadb_agentic_api/settings.py,sha256=C0YOG0y54DnZM0PufHwPtCORpjojsVTWYUTnuXo3268,1353
23
+ nucliadb_agentic_api/utils.py,sha256=SEP-F8i1NPSp03zez1iFigvtkMT9GSJDETUTpRjamHs,10575
24
+ nucliadb_agentic_api/v1/__init__.py,sha256=JVqEp_FLE855NlzWWW1MEh6iZG8rkdNE916WqFQxTgQ,313
25
+ nucliadb_agentic_api/v1/agentic_configs.py,sha256=oju9qMFeUqFstiGsqs4dp2j-74gk4sZ9cIEfS4cWDrM,3721
26
+ nucliadb_agentic_api/v1/ask.py,sha256=VCdt4YJs-YlpCBSTc0_AdaTc7Ve8M6nj5zYieSFAc5w,10530
27
+ nucliadb_agentic_api/v1/ask_websocket.py,sha256=kc5SCaXUryal9DSkXroIho1EVyqGWEyazwq0jjGJI3U,4945
28
+ nucliadb_agentic_api/v1/mcp_content.py,sha256=tSSGCTH_9IYyVmyNSH8JeIlPzVPy7w1jBD0cr4_zScQ,11109
29
+ nucliadb_agentic_api/v1/mcp_nucliadb.py,sha256=8Zmx_uwWRD7LDp5cOhBEOh0tHhJRf0aoDNATVxvYsNg,28003
30
+ nucliadb_agentic_api/v1/oauth.py,sha256=U6myUwcF1WEmkBybtZ9wHAhWaECylhiq6oB-wLo7eoI,2022
31
+ nucliadb_agentic_api/v1/router.py,sha256=WC-uHckLF4NDWjrglwto1tdjseQ-HzxM6U5f_evnoRI,60
32
+ nucliadb_agentic_api/v1/sources.py,sha256=_pRLWcg6HoDlBx68ifSWv4-PRtoDcIV6BZKp94rJDPA,4536
33
+ nucliadb_agentic_api/v1/utils.py,sha256=klmo3JZi5j--izx54E33Jj0Uqo9T929vOAlPVUoTkOU,295
34
+ nucliadb_agentic_api-1.0.0.post156.dist-info/WHEEL,sha256=LxHbYWzW7e46NaLs5XCk4QzC6UTgGl9kfPztDG7w374,81
35
+ nucliadb_agentic_api-1.0.0.post156.dist-info/entry_points.txt,sha256=yoQDJexYuWOHgHR_aGh2xxibUIKV0G5mj4dxxtb6BB0,283
36
+ nucliadb_agentic_api-1.0.0.post156.dist-info/METADATA,sha256=E0g4SMfWYZiEofTy_eV7JuhPvaJ-kYQhwkLWh3jmX2c,3057
37
+ nucliadb_agentic_api-1.0.0.post156.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: uv 0.11.30
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,6 @@
1
+ [console_scripts]
2
+ nucliadb-agentic-api = nucliadb_agentic_api.commands:run
3
+ nucliadb-agentic-api-extract-openapi = nucliadb_agentic_api.commands:extract_openapi
4
+ nucliadb-agentic-sandbox = hyperforge.server.sandbox:run
5
+ nucliadb-agentic-server = nucliadb_agentic_api.server.server:run
6
+