unique_deep_research 3.0.27__py3-none-any.whl → 3.1.0__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.
- unique_deep_research/config.py +55 -10
- unique_deep_research/service.py +6 -5
- unique_deep_research/unique_custom/agents.py +4 -2
- unique_deep_research/unique_custom/tools.py +9 -10
- unique_deep_research/unique_custom/utils.py +2 -2
- {unique_deep_research-3.0.27.dist-info → unique_deep_research-3.1.0.dist-info}/METADATA +9 -3
- {unique_deep_research-3.0.27.dist-info → unique_deep_research-3.1.0.dist-info}/RECORD +9 -9
- {unique_deep_research-3.0.27.dist-info → unique_deep_research-3.1.0.dist-info}/LICENSE +0 -0
- {unique_deep_research-3.0.27.dist-info → unique_deep_research-3.1.0.dist-info}/WHEEL +0 -0
unique_deep_research/config.py
CHANGED
|
@@ -1,13 +1,19 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from enum import StrEnum
|
|
3
3
|
from pathlib import Path
|
|
4
|
-
from typing import Literal
|
|
4
|
+
from typing import Any, Generic, Literal, TypeVar
|
|
5
5
|
|
|
6
6
|
from jinja2 import Environment, FileSystemLoader
|
|
7
|
-
from pydantic import BaseModel, Field
|
|
7
|
+
from pydantic import BaseModel, Field, field_validator
|
|
8
8
|
from unique_toolkit._common.validators import LMI, get_LMI_default_field
|
|
9
|
+
from unique_toolkit.agentic.tools.config import get_configuration_dict
|
|
9
10
|
from unique_toolkit.agentic.tools.schemas import BaseToolConfig
|
|
10
11
|
from unique_toolkit.language_model.infos import LanguageModelName
|
|
12
|
+
from unique_web_search.config import (
|
|
13
|
+
ActivatedSearchEngine,
|
|
14
|
+
DefaultSearchEngine,
|
|
15
|
+
)
|
|
16
|
+
from unique_web_search.services.search_engine import GoogleConfig
|
|
11
17
|
|
|
12
18
|
# Global template environment for the deep research tool
|
|
13
19
|
TEMPLATE_DIR = Path(__file__).parent / "templates"
|
|
@@ -32,10 +38,14 @@ class UniqueCustomEngineConfig:
|
|
|
32
38
|
RESPONSES_API_TIMEOUT_SECONDS = 3600
|
|
33
39
|
|
|
34
40
|
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
41
|
+
T = TypeVar("T", bound=DeepResearchEngine)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class BaseEngine(BaseModel, Generic[T]):
|
|
45
|
+
model_config = get_configuration_dict()
|
|
46
|
+
|
|
47
|
+
engine_type: T = Field(description="The type of engine to use for deep research")
|
|
48
|
+
|
|
39
49
|
small_model: LMI = get_LMI_default_field(
|
|
40
50
|
LanguageModelName.AZURE_GPT_4o_2024_1120,
|
|
41
51
|
description="A smaller fast model for less demanding tasks",
|
|
@@ -55,7 +65,9 @@ class BaseEngine(BaseModel):
|
|
|
55
65
|
return DeepResearchEngine(self.engine_type)
|
|
56
66
|
|
|
57
67
|
|
|
58
|
-
class OpenAIEngine(BaseEngine):
|
|
68
|
+
class OpenAIEngine(BaseEngine[Literal[DeepResearchEngine.OPENAI]]):
|
|
69
|
+
model_config = get_configuration_dict()
|
|
70
|
+
|
|
59
71
|
engine_type: Literal[DeepResearchEngine.OPENAI] = Field(
|
|
60
72
|
default=DeepResearchEngine.OPENAI
|
|
61
73
|
)
|
|
@@ -65,18 +77,51 @@ class OpenAIEngine(BaseEngine):
|
|
|
65
77
|
)
|
|
66
78
|
|
|
67
79
|
|
|
68
|
-
class
|
|
69
|
-
|
|
80
|
+
class WebToolsConfig(BaseModel):
|
|
81
|
+
model_config = get_configuration_dict()
|
|
82
|
+
|
|
83
|
+
enable: bool = Field(
|
|
70
84
|
default=True,
|
|
71
85
|
description="Allow agent to use web search tools to access the web",
|
|
72
86
|
)
|
|
87
|
+
|
|
88
|
+
search_engine: ActivatedSearchEngine = Field( # pyright: ignore[reportInvalidTypeForm]
|
|
89
|
+
default_factory=DefaultSearchEngine, # pyright: ignore[reportArgumentType]
|
|
90
|
+
description="Search Engine Configuration",
|
|
91
|
+
discriminator="search_engine_name",
|
|
92
|
+
title="Search Engine Configuration",
|
|
93
|
+
)
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
class Tools(BaseModel):
|
|
97
|
+
model_config = get_configuration_dict()
|
|
98
|
+
|
|
99
|
+
web_tools: WebToolsConfig = Field(
|
|
100
|
+
default=WebToolsConfig(),
|
|
101
|
+
description="Configuration for web search tools",
|
|
102
|
+
)
|
|
73
103
|
internal_tools: bool = Field(
|
|
74
104
|
default=True,
|
|
75
105
|
description="Allow agent to use internal search tools access information from the knowledge base and uploaded documents",
|
|
76
106
|
)
|
|
77
107
|
|
|
108
|
+
@field_validator("web_tools", mode="before")
|
|
109
|
+
@classmethod
|
|
110
|
+
def handle_bool_case(cls, v: Any) -> Any:
|
|
111
|
+
if isinstance(v, bool):
|
|
112
|
+
if v:
|
|
113
|
+
# Backward compatibility with old config behaviour
|
|
114
|
+
return WebToolsConfig(
|
|
115
|
+
enable=True, search_engine=GoogleConfig(fetch_size=10)
|
|
116
|
+
)
|
|
117
|
+
else:
|
|
118
|
+
return WebToolsConfig(enable=False)
|
|
119
|
+
return v
|
|
120
|
+
|
|
121
|
+
|
|
122
|
+
class UniqueEngine(BaseEngine[Literal[DeepResearchEngine.UNIQUE]]):
|
|
123
|
+
model_config = get_configuration_dict()
|
|
78
124
|
|
|
79
|
-
class UniqueEngine(BaseEngine):
|
|
80
125
|
engine_type: Literal[DeepResearchEngine.UNIQUE] = Field(
|
|
81
126
|
default=DeepResearchEngine.UNIQUE
|
|
82
127
|
)
|
unique_deep_research/service.py
CHANGED
|
@@ -207,7 +207,7 @@ class DeepResearchTool(Tool[DeepResearchToolConfig]):
|
|
|
207
207
|
except Exception as e:
|
|
208
208
|
if self.is_message_execution():
|
|
209
209
|
await self._update_execution_status(MessageExecutionUpdateStatus.FAILED)
|
|
210
|
-
self.logger.
|
|
210
|
+
self.logger.exception(f"Deep Research tool run failed: {e}")
|
|
211
211
|
await self.chat_service.modify_assistant_message_async(
|
|
212
212
|
content="Deep Research failed to complete for an unknown reason",
|
|
213
213
|
set_completed_at=True,
|
|
@@ -394,7 +394,7 @@ class DeepResearchTool(Tool[DeepResearchToolConfig]):
|
|
|
394
394
|
self.write_message_log_text_message("**Research done**")
|
|
395
395
|
return result
|
|
396
396
|
except Exception as e:
|
|
397
|
-
self.logger.
|
|
397
|
+
self.logger.exception(f"Research failed: {e}")
|
|
398
398
|
return "", []
|
|
399
399
|
|
|
400
400
|
async def custom_research(self, research_brief: str) -> tuple[str, list[Any]]:
|
|
@@ -427,12 +427,13 @@ class DeepResearchTool(Tool[DeepResearchToolConfig]):
|
|
|
427
427
|
enable_web_tools = True
|
|
428
428
|
enable_internal_tools = True
|
|
429
429
|
if isinstance(self.config.engine, UniqueEngine):
|
|
430
|
-
enable_web_tools = self.config.engine.tools.web_tools
|
|
430
|
+
enable_web_tools = self.config.engine.tools.web_tools.enable
|
|
431
431
|
enable_internal_tools = self.config.engine.tools.internal_tools
|
|
432
432
|
|
|
433
433
|
config = {
|
|
434
434
|
"configurable": {
|
|
435
435
|
"engine_config": self.config.engine,
|
|
436
|
+
"language_model_service": self.language_model_service,
|
|
436
437
|
"openai_client": self.client,
|
|
437
438
|
"chat_service": self.chat_service,
|
|
438
439
|
"content_service": self.content_service,
|
|
@@ -470,7 +471,7 @@ class DeepResearchTool(Tool[DeepResearchToolConfig]):
|
|
|
470
471
|
|
|
471
472
|
except Exception as e:
|
|
472
473
|
error_msg = f"Custom research failed: {str(e)}"
|
|
473
|
-
self.logger.
|
|
474
|
+
self.logger.exception(error_msg)
|
|
474
475
|
return error_msg, []
|
|
475
476
|
|
|
476
477
|
async def openai_research(self, research_brief: str) -> tuple[str, list[Any]]:
|
|
@@ -698,7 +699,7 @@ class DeepResearchTool(Tool[DeepResearchToolConfig]):
|
|
|
698
699
|
if event.response.error:
|
|
699
700
|
return event.response.error.message, []
|
|
700
701
|
except Exception as e:
|
|
701
|
-
self.logger.
|
|
702
|
+
self.logger.exception(f"Error processing research stream event: {e}")
|
|
702
703
|
|
|
703
704
|
self.logger.error("Stream ended without completion")
|
|
704
705
|
return "", []
|
|
@@ -253,7 +253,7 @@ async def supervisor_tools(
|
|
|
253
253
|
all_tool_messages.extend(research_tool_messages)
|
|
254
254
|
|
|
255
255
|
except Exception as e:
|
|
256
|
-
_LOGGER.
|
|
256
|
+
_LOGGER.exception(f"Research execution failed: {e}")
|
|
257
257
|
return Command(
|
|
258
258
|
goto="__end__",
|
|
259
259
|
update={
|
|
@@ -596,7 +596,9 @@ async def _handle_conduct_research_batch(
|
|
|
596
596
|
tool_messages = []
|
|
597
597
|
for observation, tool_call in zip(tool_results, allowed_calls):
|
|
598
598
|
if isinstance(observation, Exception):
|
|
599
|
-
_LOGGER.
|
|
599
|
+
_LOGGER.exception(
|
|
600
|
+
f"Research task failed: {str(observation)}", exc_info=observation
|
|
601
|
+
)
|
|
600
602
|
error_content = f"Research task failed: {str(observation)}"
|
|
601
603
|
tool_messages.append(
|
|
602
604
|
ToolMessage(
|
|
@@ -22,8 +22,7 @@ from unique_toolkit.chat.schemas import (
|
|
|
22
22
|
)
|
|
23
23
|
from unique_toolkit.content import ContentReference
|
|
24
24
|
from unique_toolkit.content.schemas import ContentChunk, ContentSearchType
|
|
25
|
-
from unique_web_search.
|
|
26
|
-
from unique_web_search.services.search_engine.google import GoogleConfig, GoogleSearch
|
|
25
|
+
from unique_web_search.services.search_engine import get_search_engine_service
|
|
27
26
|
|
|
28
27
|
from .utils import (
|
|
29
28
|
get_citation_manager,
|
|
@@ -153,18 +152,18 @@ async def web_search(query: str, config: RunnableConfig) -> str:
|
|
|
153
152
|
You can search again with a new query if you need more results.
|
|
154
153
|
Should be followed up by web_fetch to get the complete content of the results.
|
|
155
154
|
"""
|
|
156
|
-
|
|
155
|
+
if "configurable" not in config:
|
|
156
|
+
raise ValueError("RunnableConfig missing 'configurable' section")
|
|
157
157
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
raise ValueError("Google Search not configured")
|
|
158
|
+
configurable = config["configurable"]
|
|
159
|
+
engine_config = configurable["engine_config"].tools.web_tools.search_engine
|
|
161
160
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
161
|
+
search_engine_service = get_search_engine_service(
|
|
162
|
+
engine_config, configurable["language_model_service"]
|
|
163
|
+
)
|
|
165
164
|
|
|
166
165
|
# Perform the search
|
|
167
|
-
search_results = await
|
|
166
|
+
search_results = await search_engine_service.search(query)
|
|
168
167
|
write_tool_message_log(
|
|
169
168
|
config,
|
|
170
169
|
"**Searching the web**",
|
|
@@ -355,7 +355,7 @@ async def execute_tool_safely(
|
|
|
355
355
|
_LOGGER.warning(f"Token limit exceeded in tool {tool_name}: {str(e)}")
|
|
356
356
|
return f"Tool {tool_name} failed due to token limit: {str(e)}"
|
|
357
357
|
else:
|
|
358
|
-
_LOGGER.
|
|
358
|
+
_LOGGER.exception(f"Error executing tool {tool_name}: {str(e)}")
|
|
359
359
|
return f"Error executing tool {tool_name}: {str(e)}"
|
|
360
360
|
|
|
361
361
|
|
|
@@ -588,7 +588,7 @@ async def ainvoke_with_token_handling(
|
|
|
588
588
|
)
|
|
589
589
|
await asyncio.sleep(delay)
|
|
590
590
|
else:
|
|
591
|
-
_LOGGER.
|
|
591
|
+
_LOGGER.exception(
|
|
592
592
|
f"Model invocation failed after {max_retries + 1} attempts: {str(e)}"
|
|
593
593
|
)
|
|
594
594
|
raise e
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: unique_deep_research
|
|
3
|
-
Version: 3.0
|
|
3
|
+
Version: 3.1.0
|
|
4
4
|
Summary: Deep Research Tool for complex research tasks
|
|
5
5
|
License: Proprietary
|
|
6
6
|
Author: Martin Fadler
|
|
@@ -13,8 +13,8 @@ Requires-Dist: beautifulsoup4 (>=4.12.0,<5.0.0)
|
|
|
13
13
|
Requires-Dist: fake-useragent (>=2.0.0,<3.0.0)
|
|
14
14
|
Requires-Dist: httpx (>=0.28.0,<0.29.0)
|
|
15
15
|
Requires-Dist: jinja2 (>=3.1.2,<4.0.0)
|
|
16
|
-
Requires-Dist: langchain (>=1.1.0,<2.0.0)
|
|
17
16
|
Requires-Dist: langchain-core (>=1.0.0,<2.0.0)
|
|
17
|
+
Requires-Dist: langchain[openai] (>=1.1.0,<2.0.0)
|
|
18
18
|
Requires-Dist: langgraph (>=1.0.0,<2.0.0)
|
|
19
19
|
Requires-Dist: markdownify (>=0.14.0,<0.15.0)
|
|
20
20
|
Requires-Dist: openai (>=1.99.0,<2.0.0)
|
|
@@ -22,8 +22,8 @@ Requires-Dist: pydantic (>=2.8.2,<3.0.0)
|
|
|
22
22
|
Requires-Dist: tiktoken (>=0.12.0,<0.13.0)
|
|
23
23
|
Requires-Dist: timeout-decorator (>=0.5.0,<0.6.0)
|
|
24
24
|
Requires-Dist: typing-extensions (>=4.9.0,<5.0.0)
|
|
25
|
-
Requires-Dist: unique-toolkit (>=1.38.3,<2.0.0)
|
|
26
25
|
Requires-Dist: unique-web-search (>=1.7.0,<2.0.0)
|
|
26
|
+
Requires-Dist: unique_toolkit (>=1.38.3,<2.0.0)
|
|
27
27
|
Description-Content-Type: text/markdown
|
|
28
28
|
|
|
29
29
|
# Deep Research Tool
|
|
@@ -36,6 +36,12 @@ All notable changes to this project will be documented in this file.
|
|
|
36
36
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
37
37
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
38
38
|
|
|
39
|
+
## [3.1.00] - 2026-01-30
|
|
40
|
+
- Support other search engines than Google
|
|
41
|
+
|
|
42
|
+
## [3.0.28] - 2026-01-26
|
|
43
|
+
- Specify `langchain-openai` version
|
|
44
|
+
|
|
39
45
|
## [3.0.27] - 2026-01-23
|
|
40
46
|
- Fix bug where deep research model usage analytics were not fully exported
|
|
41
47
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
unique_deep_research/__init__.py,sha256=HMksc03A7au88XB0jjeIR3zGt3YGYTA7ZIAkR8hmQZs,396
|
|
2
|
-
unique_deep_research/config.py,sha256=
|
|
2
|
+
unique_deep_research/config.py,sha256=i6mFGQ7m0D0IYhcKsEotLX1kxl3xrHZ6cSsGITUNuug,4665
|
|
3
3
|
unique_deep_research/markdown_utils.py,sha256=zyKO4c9T_KnZzOYlYZEDEvC1NHRR9koCzcWmOJbrcok,11024
|
|
4
|
-
unique_deep_research/service.py,sha256=
|
|
4
|
+
unique_deep_research/service.py,sha256=M_dEBleP_5vqJp37dAg5VPQ5MONmfJoPO10BTogaaIg,36388
|
|
5
5
|
unique_deep_research/templates/clarifying_agent.j2,sha256=TOgD5ezrlRyurpquBGT902oa7PpkdHxsvrkIFWBFa2A,3938
|
|
6
6
|
unique_deep_research/templates/openai/oai_research_system_message.j2,sha256=2YipVh0V4eEikg9iXqIaBVtYwS8ycHMDu3xiFDoJ0vI,1362
|
|
7
7
|
unique_deep_research/templates/report_cleanup_prompt.j2,sha256=9mjj3GX1HEQ0kkxzqvB3D_2wFgAJ-drMbTYDiSc2riI,2049
|
|
@@ -11,12 +11,12 @@ unique_deep_research/templates/unique/lead_agent_system.j2,sha256=tWxljNKiQd5i8P
|
|
|
11
11
|
unique_deep_research/templates/unique/report_writer_system_open_deep_research.j2,sha256=7Ojg144Z0RfH8xLalA068oTV_jAUwxAGEjm3YtAdQac,6365
|
|
12
12
|
unique_deep_research/templates/unique/research_agent_system.j2,sha256=D4mPHVUaPRPo-ElbE_FR0P3zS6BGNsoSohz4FkCdnx8,7981
|
|
13
13
|
unique_deep_research/unique_custom/__init__.py,sha256=S1Rw16tPDfqOIZwAoJ6lAJT00Pk8uh36Aa-otM8vST0,1370
|
|
14
|
-
unique_deep_research/unique_custom/agents.py,sha256=
|
|
14
|
+
unique_deep_research/unique_custom/agents.py,sha256=zMJ8zCz-0714bMPZTzIlMqG79AdZAf5orEIJbPLIO3U,24039
|
|
15
15
|
unique_deep_research/unique_custom/citation.py,sha256=dfElOqBTvbXlHXxshT6heNzeHVVKTlgSlIEFTuoNv20,4315
|
|
16
16
|
unique_deep_research/unique_custom/state.py,sha256=tn4pIKN-ZCH6Tqu9XL31lziyrwFkhTVf_lBiUpETVyo,3515
|
|
17
|
-
unique_deep_research/unique_custom/tools.py,sha256=
|
|
18
|
-
unique_deep_research/unique_custom/utils.py,sha256=
|
|
19
|
-
unique_deep_research-3.0.
|
|
20
|
-
unique_deep_research-3.0.
|
|
21
|
-
unique_deep_research-3.0.
|
|
22
|
-
unique_deep_research-3.0.
|
|
17
|
+
unique_deep_research/unique_custom/tools.py,sha256=QEjdKc44hMGpZ8SiF25nrgTvqeu9d_0zd2t27V7LBjY,25424
|
|
18
|
+
unique_deep_research/unique_custom/utils.py,sha256=ZOKABCVKODawfsig-72CcpXCMveg7K_y_rzwlkaJVz8,20069
|
|
19
|
+
unique_deep_research-3.1.0.dist-info/LICENSE,sha256=GlN8wHNdh53xwOPg44URnwag6TEolCjoq3YD_KrWgss,193
|
|
20
|
+
unique_deep_research-3.1.0.dist-info/METADATA,sha256=Mu0KA77LolMjv99raWziiejX8Y1A7XVr-CjnfC63dXI,7167
|
|
21
|
+
unique_deep_research-3.1.0.dist-info/WHEEL,sha256=sP946D7jFCHeNz5Iq4fL4Lu-PrWrFsgfLXbbkciIZwg,88
|
|
22
|
+
unique_deep_research-3.1.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|