hyperforge 1.0.0.post50__tar.gz → 1.0.0.post56__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.
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/PKG-INFO +3 -3
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/pyproject.toml +3 -3
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/models.py +5 -1
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/v1/interaction.py +1 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/broker/redis.py +1 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/engine.py +6 -2
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/manager.py +3 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/memory/memory.py +49 -25
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/pubsub.py +6 -1
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/server/session.py +1 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/README.md +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/VERSION +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/__init__.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/agent.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/alembic/README +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/alembic/env.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/alembic/script.py.mako +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/alembic/versions/049a4aed19e1_create_generation_table.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/alembic/versions/1416cb41bd49_add_downloads_table.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/alembic/versions/2847934e2d59_unique_driver.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/alembic/versions/4d89a36c8bda_memory.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/alembic/versions/72b997edb05d_identifier_for_drivers.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/alembic/versions/9c6f6a1b4e7f_workflows_default_fk.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/alembic/versions/b8edb72295e0_.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/alembic/versions/f3d2c1b0a9e8_soft_delete_workflows.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/alembic.ini +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/__init__.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/app.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/authentication.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/commands.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/internal/__init__.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/internal/inspect.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/internal/router.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/logging.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/session.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/settings.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/utils.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/v1/__init__.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/v1/agents.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/v1/mcp_content.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/v1/mcp_interaction.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/v1/oauth.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/v1/prompt.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/v1/router.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/v1/schema.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/v1/session.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/v1/utils.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/v1/workflows.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/arag.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/broker/__init__.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/broker/local.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/configure.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/context/__init__.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/context/agent.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/context/config.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/database.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/db/__init__.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/db/agents.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/db/encryption.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/db/exceptions.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/db/settings.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/db/workflow_cleanup.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/definition.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/driver.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/dummy.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/exceptions.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/feature_flag.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/fixtures.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/interaction.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/llm.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/memory/__init__.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/minimal_fixtures.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/models.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/nua.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/openapi.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/prompts.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/py.typed +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/redis_utils.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/retrieval/__init__.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/retrieval/agent.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/retrieval/config.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/server/__init__.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/server/cache.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/server/run.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/server/sandbox.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/server/settings.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/server/utils.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/server/web.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/settings.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/standalone/__init__.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/standalone/agent.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/standalone/app.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/standalone/config.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/standalone/const.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/standalone/run.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/standalone/settings.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/standalone/ui_router.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/static/assets/index-DLejS5jC.js +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/static/assets/index-MoxkZ1_3.css +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/static/favicon.svg +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/static/index.html +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/trace.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/utils/__init__.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/utils/http.py +0 -0
- {hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/workflows.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hyperforge
|
|
3
|
-
Version: 1.0.0.
|
|
3
|
+
Version: 1.0.0.post56
|
|
4
4
|
Summary: Agentic Framework for Orchestrated Runtime, Governance, and Execution
|
|
5
5
|
Author: AI Data Team
|
|
6
6
|
Author-email: AI Data Team <learning@nuclia.com>
|
|
@@ -9,8 +9,8 @@ Classifier: Programming Language :: Python :: 3
|
|
|
9
9
|
Classifier: Operating System :: OS Independent
|
|
10
10
|
Requires-Dist: alembic
|
|
11
11
|
Requires-Dist: pydantic
|
|
12
|
-
Requires-Dist: nuclia>=4.
|
|
13
|
-
Requires-Dist: nuclia-models>=0.
|
|
12
|
+
Requires-Dist: nuclia>=4.11.5
|
|
13
|
+
Requires-Dist: nuclia-models>=0.59.0
|
|
14
14
|
Requires-Dist: nucliadb-models>=6.13.1.post6414
|
|
15
15
|
Requires-Dist: nucliadb-sdk>=6.13.1.post6414
|
|
16
16
|
Requires-Dist: nucliadb-telemetry[otel]
|
|
@@ -4,7 +4,7 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "hyperforge"
|
|
7
|
-
version = "1.0.0.
|
|
7
|
+
version = "1.0.0.post56"
|
|
8
8
|
authors = [{ name = "AI Data Team", email = "learning@nuclia.com" }]
|
|
9
9
|
description = "Agentic Framework for Orchestrated Runtime, Governance, and Execution"
|
|
10
10
|
readme = "README.md"
|
|
@@ -17,8 +17,8 @@ license = "Apache-2.0"
|
|
|
17
17
|
dependencies = [
|
|
18
18
|
"alembic",
|
|
19
19
|
"pydantic",
|
|
20
|
-
"nuclia>=4.
|
|
21
|
-
"nuclia-models>=0.
|
|
20
|
+
"nuclia>=4.11.5",
|
|
21
|
+
"nuclia-models>=0.59.0",
|
|
22
22
|
"nucliadb-models>=6.13.1.post6414",
|
|
23
23
|
"nucliadb-sdk>=6.13.1.post6414",
|
|
24
24
|
"nucliadb-telemetry[otel]",
|
|
@@ -5,7 +5,7 @@ from nucliadb_models import TextFormat
|
|
|
5
5
|
from pydantic import BaseModel, Field
|
|
6
6
|
|
|
7
7
|
from hyperforge.driver import DriverConfig
|
|
8
|
-
from hyperforge.models import Rules
|
|
8
|
+
from hyperforge.models import HistoryQuestionAnswer, Rules
|
|
9
9
|
|
|
10
10
|
|
|
11
11
|
class StashRoles(str, Enum):
|
|
@@ -125,5 +125,9 @@ class InteractionRequest(BaseModel):
|
|
|
125
125
|
question: str
|
|
126
126
|
headers: Dict[str, str] = {}
|
|
127
127
|
arguments: Dict[str, str] = {}
|
|
128
|
+
chat_history: Optional[List[HistoryQuestionAnswer]] = Field(
|
|
129
|
+
default=None,
|
|
130
|
+
description="Client-managed chat history. When set (even to an empty list), overrides any server-side session history for agents that use previous Q&A context (rephrase, summarize, smart, etc.). Omit the field entirely to use server-side session history.",
|
|
131
|
+
)
|
|
128
132
|
operation: InteractionOperation = InteractionOperation.QUESTION
|
|
129
133
|
streaming: bool = False
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
from dataclasses import dataclass
|
|
3
|
-
from typing import Any, Awaitable, Callable, Dict, Optional, Tuple, cast
|
|
3
|
+
from typing import Any, Awaitable, Callable, Dict, List, Optional, Tuple, cast
|
|
4
4
|
|
|
5
5
|
from nuclia.lib.nua import AsyncNuaClient
|
|
6
6
|
|
|
@@ -9,6 +9,7 @@ from hyperforge.interaction import AragAnswer
|
|
|
9
9
|
from hyperforge.llm import NoopNuaClient, NuaBaseModel, NUAConnection
|
|
10
10
|
from hyperforge.manager import Manager
|
|
11
11
|
from hyperforge.memory.memory import BaseSessionMemory, QuestionMemory, SessionMemory
|
|
12
|
+
from hyperforge.models import HistoryQuestionAnswer
|
|
12
13
|
from hyperforge.retrieval.agent import RetrievalAgent
|
|
13
14
|
from hyperforge.retrieval.config import RetrievalAgentConfig
|
|
14
15
|
|
|
@@ -84,6 +85,7 @@ async def main(
|
|
|
84
85
|
headers: Optional[Dict[str, str]] = None,
|
|
85
86
|
memory_klass: type[BaseSessionMemory] = SessionMemory,
|
|
86
87
|
streaming: bool = False,
|
|
88
|
+
chat_history: Optional[List[HistoryQuestionAnswer]] = None,
|
|
87
89
|
) -> QuestionMemory:
|
|
88
90
|
try:
|
|
89
91
|
state, session_memory = await init(
|
|
@@ -99,7 +101,9 @@ async def main(
|
|
|
99
101
|
session_id=session_id,
|
|
100
102
|
memory_klass=memory_klass,
|
|
101
103
|
)
|
|
102
|
-
question_memory = session_memory.start_question(
|
|
104
|
+
question_memory = session_memory.start_question(
|
|
105
|
+
question, streaming=streaming, chat_history=chat_history
|
|
106
|
+
)
|
|
103
107
|
if callback is not None:
|
|
104
108
|
question_memory.set_callback_fn(callback)
|
|
105
109
|
|
|
@@ -196,6 +196,9 @@ class Manager:
|
|
|
196
196
|
result.object = c.object
|
|
197
197
|
elif isinstance(c, MetaGenerativeResponse):
|
|
198
198
|
result.timings = c.timings
|
|
199
|
+
result.learning_id = c.learning_id
|
|
200
|
+
result.model_name = c.model_name
|
|
201
|
+
result.trace_id = c.trace_id
|
|
199
202
|
elif isinstance(c, CitationsGenerativeResponse):
|
|
200
203
|
result.citations = c.citations
|
|
201
204
|
elif isinstance(c, FootnoteCitationsGenerativeResponse):
|
|
@@ -54,6 +54,26 @@ logger = logging.getLogger("arag.memory")
|
|
|
54
54
|
|
|
55
55
|
# NucliaDB storage
|
|
56
56
|
QUESTION_ANSWERS_FIELD: str = "qas"
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def _qa_list_to_context_string(history: List[HistoryQuestionAnswer]) -> Tuple[str, int]:
|
|
60
|
+
"""Format a list of Q&A pairs into the prompt context string used by agents."""
|
|
61
|
+
result = "".join(
|
|
62
|
+
f"- Question: {qa.question}\n- Answer: {qa.answer}\n" for qa in history
|
|
63
|
+
)
|
|
64
|
+
return result, len(history)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def _qa_list_to_chat_messages(history: List[HistoryQuestionAnswer]) -> List[Message]:
|
|
68
|
+
"""Convert a list of Q&A pairs into the alternating User/Nuclia Message list used by LLMs."""
|
|
69
|
+
return [
|
|
70
|
+
msg
|
|
71
|
+
for qa in history
|
|
72
|
+
for msg in (
|
|
73
|
+
Message(author=Author.USER, text=qa.question),
|
|
74
|
+
Message(author=Author.NUCLIA, text=qa.answer),
|
|
75
|
+
)
|
|
76
|
+
]
|
|
57
77
|
CONTEXT_FIELD: str = "context"
|
|
58
78
|
STEPS_FIELD: str = "steps"
|
|
59
79
|
USER_INFO_FIELD: str = "user_info"
|
|
@@ -112,34 +132,13 @@ class BaseSessionMemory:
|
|
|
112
132
|
return KnowledgeboxFindResults(total=0, resources={})
|
|
113
133
|
|
|
114
134
|
async def get_chat_history(self) -> List[Message]:
|
|
115
|
-
|
|
116
|
-
result = []
|
|
117
|
-
for qa in qas:
|
|
118
|
-
result.append(
|
|
119
|
-
Message(
|
|
120
|
-
author=Author.USER,
|
|
121
|
-
text=qa.question,
|
|
122
|
-
)
|
|
123
|
-
)
|
|
124
|
-
result.append(
|
|
125
|
-
Message(
|
|
126
|
-
author=Author.NUCLIA,
|
|
127
|
-
text=qa.answer,
|
|
128
|
-
)
|
|
129
|
-
)
|
|
130
|
-
return result
|
|
135
|
+
return _qa_list_to_chat_messages(await self.qa_history())
|
|
131
136
|
|
|
132
137
|
async def qa_history(self) -> list[HistoryQuestionAnswer]:
|
|
133
138
|
return []
|
|
134
139
|
|
|
135
140
|
async def context_history(self) -> Tuple[str, int]:
|
|
136
|
-
|
|
137
|
-
interactions = 0
|
|
138
|
-
for qa in await self.qa_history():
|
|
139
|
-
result += f"- Question: {qa.question}\n"
|
|
140
|
-
result += f"- Answer: {qa.answer}\n"
|
|
141
|
-
interactions += 1
|
|
142
|
-
return result, interactions
|
|
141
|
+
return _qa_list_to_context_string(await self.qa_history())
|
|
143
142
|
|
|
144
143
|
def start_question(
|
|
145
144
|
self,
|
|
@@ -149,6 +148,7 @@ class BaseSessionMemory:
|
|
|
149
148
|
headers: Dict[str, str] = {},
|
|
150
149
|
arguments: Dict[str, str] = {},
|
|
151
150
|
streaming: bool = False,
|
|
151
|
+
chat_history: Optional[List[HistoryQuestionAnswer]] = None,
|
|
152
152
|
) -> "QuestionMemory":
|
|
153
153
|
return QuestionMemory(
|
|
154
154
|
self,
|
|
@@ -158,6 +158,7 @@ class BaseSessionMemory:
|
|
|
158
158
|
headers=headers,
|
|
159
159
|
arguments=arguments,
|
|
160
160
|
streaming=streaming,
|
|
161
|
+
chat_history=chat_history,
|
|
161
162
|
)
|
|
162
163
|
|
|
163
164
|
async def save(self, question: "QuestionMemory") -> None:
|
|
@@ -183,6 +184,7 @@ class NoMemorySessionMemory(BaseSessionMemory):
|
|
|
183
184
|
headers: Dict[str, str] = {},
|
|
184
185
|
arguments: Dict[str, str] = {},
|
|
185
186
|
streaming: bool = False,
|
|
187
|
+
chat_history: Optional[List[HistoryQuestionAnswer]] = None,
|
|
186
188
|
) -> "QuestionMemory":
|
|
187
189
|
return QuestionMemory(
|
|
188
190
|
self,
|
|
@@ -192,6 +194,7 @@ class NoMemorySessionMemory(BaseSessionMemory):
|
|
|
192
194
|
headers=headers,
|
|
193
195
|
arguments=arguments,
|
|
194
196
|
streaming=streaming,
|
|
197
|
+
chat_history=chat_history,
|
|
195
198
|
)
|
|
196
199
|
|
|
197
200
|
async def save(self, question: "QuestionMemory") -> None:
|
|
@@ -250,6 +253,7 @@ class EphemeralSessionMemory(BaseSessionMemory):
|
|
|
250
253
|
headers: Dict[str, str] = {},
|
|
251
254
|
arguments: Dict[str, str] = {},
|
|
252
255
|
streaming: bool = False,
|
|
256
|
+
chat_history: Optional[List[HistoryQuestionAnswer]] = None,
|
|
253
257
|
) -> "QuestionMemory":
|
|
254
258
|
return QuestionMemory(
|
|
255
259
|
self,
|
|
@@ -259,6 +263,7 @@ class EphemeralSessionMemory(BaseSessionMemory):
|
|
|
259
263
|
headers=headers,
|
|
260
264
|
arguments=arguments,
|
|
261
265
|
streaming=streaming,
|
|
266
|
+
chat_history=chat_history,
|
|
262
267
|
)
|
|
263
268
|
|
|
264
269
|
async def save(self, question: "QuestionMemory") -> None:
|
|
@@ -580,10 +585,21 @@ class QuestionMemory:
|
|
|
580
585
|
headers: Dict[str, str] | None = None,
|
|
581
586
|
arguments: Dict[str, str] | None = None,
|
|
582
587
|
streaming: bool = False,
|
|
588
|
+
chat_history: Optional[List[HistoryQuestionAnswer]] = None,
|
|
583
589
|
):
|
|
584
590
|
self.session = session
|
|
585
591
|
self.started_at = datetime.now(timezone.utc)
|
|
586
592
|
|
|
593
|
+
# Client-managed chat history. When set (even to an empty list), overrides
|
|
594
|
+
# server-side session history for agents that use previous Q&A context
|
|
595
|
+
# (rephrase, summarize, smart, etc.). None means "not set — use server-side
|
|
596
|
+
# history". [] means "override with no history". Intended for ephemeral
|
|
597
|
+
# sessions where the client is responsible for maintaining conversation state.
|
|
598
|
+
# Note: search_in_questions() performs semantic search over NucliaDB-stored
|
|
599
|
+
# conversation history and is NOT affected by this field. The HistoricalAgent
|
|
600
|
+
# uses that method and therefore does not benefit from client-managed history.
|
|
601
|
+
self._client_chat_history: Optional[List[HistoryQuestionAnswer]] = chat_history
|
|
602
|
+
|
|
587
603
|
# Start of a new question by the user
|
|
588
604
|
self.original_question = question
|
|
589
605
|
if actions is not None:
|
|
@@ -649,11 +665,19 @@ class QuestionMemory:
|
|
|
649
665
|
return await self.session.get_source(source_id)
|
|
650
666
|
|
|
651
667
|
async def context_history(self) -> Tuple[str, int]:
|
|
652
|
-
"""Returns a string with the context history of the conversation. This can include information such as previous questions and answers, relevant information that has been previously discussed in the conversation, or any other relevant information that can help the agent to generate a more accurate and personalized response.
|
|
668
|
+
"""Returns a string with the context history of the conversation. This can include information such as previous questions and answers, relevant information that has been previously discussed in the conversation, or any other relevant information that can help the agent to generate a more accurate and personalized response.
|
|
669
|
+
|
|
670
|
+
When the client sets chat_history in the request (even to an empty list), it overrides any server-side session history. None means "not set — use server-side history"."""
|
|
671
|
+
if self._client_chat_history is not None:
|
|
672
|
+
return _qa_list_to_context_string(self._client_chat_history)
|
|
653
673
|
return await self.session.context_history()
|
|
654
674
|
|
|
655
675
|
async def get_chat_history(self) -> list[Message]:
|
|
656
|
-
"""Returns a list of tuples with the chat history of the conversation. Each tuple contains a question and an answer. This can be used to keep track of the conversation history in a more structured way, and to provide more context to the agent when generating a response.
|
|
676
|
+
"""Returns a list of tuples with the chat history of the conversation. Each tuple contains a question and an answer. This can be used to keep track of the conversation history in a more structured way, and to provide more context to the agent when generating a response.
|
|
677
|
+
|
|
678
|
+
When the client sets chat_history in the request (even to an empty list), it overrides any server-side session history. None means "not set — use server-side history"."""
|
|
679
|
+
if self._client_chat_history is not None:
|
|
680
|
+
return _qa_list_to_chat_messages(self._client_chat_history)
|
|
657
681
|
return await self.session.get_chat_history()
|
|
658
682
|
|
|
659
683
|
def stats(self):
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
from typing import Annotated, Dict, Literal
|
|
1
|
+
from typing import Annotated, Dict, List, Literal, Optional
|
|
2
2
|
|
|
3
3
|
from pydantic import AliasChoices, BaseModel, Field
|
|
4
4
|
from pydantic.types import Discriminator, Tag
|
|
@@ -8,6 +8,7 @@ from hyperforge.interaction import (
|
|
|
8
8
|
Feedback,
|
|
9
9
|
OAuthAuthenticateURL,
|
|
10
10
|
)
|
|
11
|
+
from hyperforge.models import HistoryQuestionAnswer
|
|
11
12
|
|
|
12
13
|
# Messages used in the pubsub protocol between API and agent servers
|
|
13
14
|
|
|
@@ -22,6 +23,10 @@ class StartInteraction(BaseModel):
|
|
|
22
23
|
question: str
|
|
23
24
|
headers: Dict[str, str] = {}
|
|
24
25
|
arguments: Dict[str, str] = {}
|
|
26
|
+
chat_history: Optional[List[HistoryQuestionAnswer]] = Field(
|
|
27
|
+
default=None,
|
|
28
|
+
description="Client-managed chat history. When set (even to an empty list), overrides any server-side session history for agents that use previous Q&A context. Omit the field entirely to use server-side session history.",
|
|
29
|
+
)
|
|
25
30
|
workflow_id: str = "default"
|
|
26
31
|
streaming: bool = False
|
|
27
32
|
op: Literal["start"] = "start"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/alembic/versions/b8edb72295e0_.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/api/v1/mcp_interaction.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/static/assets/index-DLejS5jC.js
RENAMED
|
File without changes
|
{hyperforge-1.0.0.post50 → hyperforge-1.0.0.post56}/src/hyperforge/static/assets/index-MoxkZ1_3.css
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|