alita-sdk 0.3.176__py3-none-any.whl → 0.3.177__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 (41) hide show
  1. alita_sdk/community/__init__.py +7 -17
  2. alita_sdk/tools/carrier/api_wrapper.py +6 -0
  3. alita_sdk/tools/carrier/backend_tests_tool.py +308 -7
  4. alita_sdk/tools/carrier/carrier_sdk.py +18 -0
  5. alita_sdk/tools/carrier/tools.py +2 -1
  6. {alita_sdk-0.3.176.dist-info → alita_sdk-0.3.177.dist-info}/METADATA +1 -2
  7. {alita_sdk-0.3.176.dist-info → alita_sdk-0.3.177.dist-info}/RECORD +10 -41
  8. alita_sdk/community/browseruse/__init__.py +0 -73
  9. alita_sdk/community/browseruse/api_wrapper.py +0 -288
  10. alita_sdk/community/deep_researcher/__init__.py +0 -70
  11. alita_sdk/community/deep_researcher/agents/__init__.py +0 -1
  12. alita_sdk/community/deep_researcher/agents/baseclass.py +0 -182
  13. alita_sdk/community/deep_researcher/agents/knowledge_gap_agent.py +0 -74
  14. alita_sdk/community/deep_researcher/agents/long_writer_agent.py +0 -251
  15. alita_sdk/community/deep_researcher/agents/planner_agent.py +0 -124
  16. alita_sdk/community/deep_researcher/agents/proofreader_agent.py +0 -80
  17. alita_sdk/community/deep_researcher/agents/thinking_agent.py +0 -64
  18. alita_sdk/community/deep_researcher/agents/tool_agents/__init__.py +0 -20
  19. alita_sdk/community/deep_researcher/agents/tool_agents/crawl_agent.py +0 -87
  20. alita_sdk/community/deep_researcher/agents/tool_agents/search_agent.py +0 -96
  21. alita_sdk/community/deep_researcher/agents/tool_selector_agent.py +0 -83
  22. alita_sdk/community/deep_researcher/agents/utils/__init__.py +0 -0
  23. alita_sdk/community/deep_researcher/agents/utils/parse_output.py +0 -148
  24. alita_sdk/community/deep_researcher/agents/writer_agent.py +0 -63
  25. alita_sdk/community/deep_researcher/api_wrapper.py +0 -116
  26. alita_sdk/community/deep_researcher/deep_research.py +0 -185
  27. alita_sdk/community/deep_researcher/examples/deep_example.py +0 -30
  28. alita_sdk/community/deep_researcher/examples/iterative_example.py +0 -34
  29. alita_sdk/community/deep_researcher/examples/report_plan_example.py +0 -27
  30. alita_sdk/community/deep_researcher/iterative_research.py +0 -419
  31. alita_sdk/community/deep_researcher/llm_config.py +0 -87
  32. alita_sdk/community/deep_researcher/main.py +0 -67
  33. alita_sdk/community/deep_researcher/tools/__init__.py +0 -2
  34. alita_sdk/community/deep_researcher/tools/crawl_website.py +0 -109
  35. alita_sdk/community/deep_researcher/tools/web_search.py +0 -294
  36. alita_sdk/community/deep_researcher/utils/__init__.py +0 -0
  37. alita_sdk/community/deep_researcher/utils/md_to_pdf.py +0 -8
  38. alita_sdk/community/deep_researcher/utils/os.py +0 -21
  39. {alita_sdk-0.3.176.dist-info → alita_sdk-0.3.177.dist-info}/WHEEL +0 -0
  40. {alita_sdk-0.3.176.dist-info → alita_sdk-0.3.177.dist-info}/licenses/LICENSE +0 -0
  41. {alita_sdk-0.3.176.dist-info → alita_sdk-0.3.177.dist-info}/top_level.txt +0 -0
@@ -1,288 +0,0 @@
1
- from datetime import datetime
2
- from typing import Dict, List, Any, Optional, Type
3
- from pydantic import BaseModel, Field
4
- from browser_use import Agent, ActionResult, Browser, BrowserConfig, BrowserContextConfig
5
- from browser_use.agent.views import AgentHistoryList
6
- from playwright._impl._api_structures import ProxySettings
7
- from alita_sdk.tools.elitea_base import BaseToolApiWrapper
8
- from pydantic import create_model, Field, model_validator
9
- from tempfile import TemporaryDirectory, NamedTemporaryFile
10
- from browser_use.controller.service import Controller
11
- from langchain_core.callbacks import dispatch_custom_event
12
- from pyobjtojson import obj_to_json
13
- import os
14
- import asyncio
15
-
16
- import socket
17
- from browser_use.browser.utils.screen_resolution import get_screen_resolution, get_window_adjustments
18
- from playwright.async_api import Playwright, Browser as PlaywrightBrowser
19
- from browser_use.browser.chrome import (
20
- CHROME_ARGS,
21
- CHROME_DEBUG_PORT,
22
- CHROME_DETERMINISTIC_RENDERING_ARGS,
23
- CHROME_DISABLE_SECURITY_ARGS,
24
- CHROME_DOCKER_ARGS,
25
- CHROME_HEADLESS_ARGS,
26
- )
27
- IN_DOCKER = os.environ.get('IN_DOCKER', 'false').lower()[0] in 'ty1'
28
-
29
- class BrowserEx(Browser):
30
- def __init__(self, config: BrowserConfig):
31
- super().__init__(config)
32
- self.config = config
33
-
34
- async def _setup_builtin_browser(self, playwright: Playwright) -> PlaywrightBrowser:
35
- """Sets up and returns a Playwright Browser instance with anti-detection measures."""
36
- assert self.config.browser_binary_path is None, 'browser_binary_path should be None if trying to use the builtin browsers'
37
-
38
- # Use the configured window size from new_context_config if available
39
- if (
40
- not self.config.headless
41
- and hasattr(self.config, 'new_context_config')
42
- and hasattr(self.config.new_context_config, 'browser_window_size')
43
- ):
44
- screen_size = self.config.new_context_config.browser_window_size.model_dump()
45
- offset_x, offset_y = get_window_adjustments()
46
- elif self.config.headless:
47
- screen_size = {'width': 1920, 'height': 1080}
48
- offset_x, offset_y = 0, 0
49
- else:
50
- screen_size = get_screen_resolution()
51
- offset_x, offset_y = get_window_adjustments()
52
-
53
- chrome_args = {
54
- f'--remote-debugging-port={self.config.chrome_remote_debugging_port}',
55
- *CHROME_ARGS,
56
- *(CHROME_DOCKER_ARGS if IN_DOCKER else []),
57
- *(CHROME_HEADLESS_ARGS if self.config.headless else []),
58
- *(CHROME_DISABLE_SECURITY_ARGS if self.config.disable_security else []),
59
- *(CHROME_DETERMINISTIC_RENDERING_ARGS if self.config.deterministic_rendering else []),
60
- f'--window-position={offset_x},{offset_y}',
61
- f'--window-size={screen_size["width"]},{screen_size["height"]}',
62
- *self.config.extra_browser_args,
63
- }
64
-
65
- # check if chrome remote debugging port is already taken,
66
- # if so remove the remote-debugging-port arg to prevent conflicts
67
- with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s:
68
- if s.connect_ex(('localhost', self.config.chrome_remote_debugging_port)) == 0:
69
- chrome_args.remove(f'--remote-debugging-port={self.config.chrome_remote_debugging_port}')
70
-
71
- browser_class = getattr(playwright, self.config.browser_class)
72
- args = {
73
- 'chromium': list(chrome_args),
74
- 'firefox': [
75
- *{
76
- '-no-remote',
77
- *self.config.extra_browser_args,
78
- }
79
- ],
80
- 'webkit': [
81
- *{
82
- '--no-startup-window',
83
- *self.config.extra_browser_args,
84
- }
85
- ],
86
- }
87
-
88
- browser = await browser_class.launch(
89
- headless=self.config.headless,
90
- channel='chromium',
91
- args=args[self.config.browser_class],
92
- proxy=self.config.proxy.model_dump() if self.config.proxy else None,
93
- handle_sigterm=False,
94
- handle_sigint=False,
95
- )
96
- return browser
97
-
98
-
99
- BrowserTask = create_model(
100
- "BrowserTask",
101
- task=(str, Field(description="Task to perform")),
102
- max_steps=(Optional[int], Field(description="Maximum number of steps to perform")),
103
- debug=(Optional[bool], Field(description="Whether debug mode is enabled")),
104
- __config__=Field(description="Browser Use API Wrapper")
105
- )
106
-
107
- BrowserTasks = create_model(
108
- "BrowserTasks",
109
- tasks=(List[str], Field(description="List of tasks to perform")),
110
- max_steps=(Optional[int], Field(description="Maximum number of steps to perform")),
111
- debug=(Optional[bool], Field(description="Whether debug mode is enabled")),
112
- __config__=Field(description="Browser Use API Wrapper")
113
- )
114
-
115
- async def thinking_processor(agent):
116
- """Hook to be called after each step."""
117
- if hasattr(agent, "state"):
118
- history = agent.state.history
119
- else:
120
- history = None
121
- return
122
-
123
- # Process model thoughts
124
- model_thoughts = obj_to_json(
125
- obj=history.model_thoughts(),
126
- check_circular=False
127
- )
128
- if len(model_thoughts) > 0:
129
- model_thoughts_last_elem = model_thoughts[-1]
130
- evalualtion = model_thoughts_last_elem.get('evaluation_previous_goal')
131
- memory = model_thoughts_last_elem.get('memory')
132
- next_goal = model_thoughts_last_elem.get('next_goal')
133
- dispatch_custom_event(
134
- name="thinking_step",
135
- data={
136
- "message": f"**Memory** : \n\n{memory}\n\n**Evaluation goal**:\n\n{evalualtion}\n\n**Next goal**:\n\n{next_goal}",
137
- "tool_name": "task",
138
- "toolkit": "browser_use"
139
- }
140
- )
141
-
142
-
143
- class DoneResult(BaseModel):
144
- title: str
145
- comments: str
146
- hours_since_start: int
147
-
148
- gif_default_location = './agent_history.gif'
149
- default_bucket = 'browseruse'
150
-
151
- class BrowserUseAPIWrapper(BaseToolApiWrapper):
152
- """Wrapper for Browser Use API."""
153
- headless: bool = True
154
- width: int = 1280
155
- height: int = 800
156
- use_vision: bool = False
157
- trace_actions: bool = False
158
- trace_actions_path: Optional[str] = None
159
- cookies: Optional[Dict[str, Any]] = None
160
- disable_security: bool = True
161
- proxy: Any = None
162
- extra_chromium_args: List[str] = []
163
- client: Any = None # AlitaClient
164
- artifact: Any = None # Artifact
165
- llm: Any = None # LLMLikeObject
166
- bucket: str = None
167
- proxy_settings: Any = None
168
- validate_output: bool = False
169
- planner_llm: Any = None
170
- browser_window_size: Dict[str, int] = None
171
-
172
-
173
- @model_validator(mode='before')
174
- @classmethod
175
- def validate_toolkit(cls, values):
176
- """Validate toolkit parameters."""
177
- values['proxy'] = ProxySettings(**values['proxy']) if values.get('proxy') else None
178
- values['extra_chromium_args'] = values.get('extra_chromium_args') or []
179
- values['browser_window_size'] = {"width": values.get('width', 1280), "height": values.get('height', 800)}
180
- values['artifact'] = values.get('client').artifact(values.get('bucket', default_bucket))
181
- return values
182
-
183
-
184
- def _create_browser(self):
185
- cookies_file = None
186
- if self.cookies:
187
- cookies_file = NamedTemporaryFile(delete=False)
188
- cookies_file.write(self.cookies)
189
- cookies_file.close()
190
-
191
- context_config = BrowserContextConfig(
192
- cookies_file=cookies_file,
193
- wait_for_network_idle_page_load_time=10.0, # TODO: Make this configurable
194
- highlight_elements=True,
195
- browser_window_size=self.browser_window_size
196
- )
197
- browser_config = BrowserConfig(
198
- headless=self.headless,
199
- browser_class='chromium', # TODO: Make this configurable
200
- disable_security=self.disable_security,
201
- extra_chromium_args=self.extra_chromium_args,
202
- proxy=self.proxy,
203
- new_context_config=context_config
204
- )
205
- return BrowserEx(config=browser_config)
206
-
207
- def task(self, task: str, max_steps: Optional[int] = 20, debug: Optional[bool] = False):
208
- """Perform a task using the browser."""
209
- return asyncio.run(self._tasks([task], max_steps, debug))
210
-
211
- async def _tasks(self, tasks: List[str], max_steps: Optional[int] = 20, debug: Optional[bool] = False):
212
- browser = self._create_browser()
213
- context_config = BrowserContextConfig(
214
- wait_for_network_idle_page_load_time=10.0, # TODO: Make this configurable
215
- highlight_elements=True,
216
- browser_window_size=self.browser_window_size
217
- )
218
- async with await browser.new_context(context_config) as browser:
219
- start = tasks[0]
220
- if len(tasks) == 1:
221
- tasks = []
222
- agent = Agent(
223
- task=start,
224
- llm=self.llm,
225
- browser_context=browser,
226
- max_actions_per_step=20,
227
- use_vision=self.use_vision,
228
- save_conversation_path=None,
229
- generate_gif=True,
230
- planner_llm=self.planner_llm,
231
- controller=Controller(),
232
- message_context = "Carefully check every step, and make sure to provide detailed feedback on the results.",
233
- validate_output=self.validate_output
234
- )
235
- for task in tasks:
236
- agent.add_new_task(task)
237
- history: AgentHistoryList = await agent.run(
238
- max_steps=max_steps,
239
- on_step_end=thinking_processor
240
- )
241
- await browser.close()
242
- files = self._save_execution(history.model_dump_json())
243
-
244
- return {
245
- "run_data": str(history.extracted_content()),
246
- "files": files
247
- }
248
-
249
- def _save_execution(self, data_content: Any):
250
- """Saves tasks execution gif"""
251
-
252
- try:
253
- with open(gif_default_location, 'rb') as file:
254
- artifact_data = file.read()
255
- except FileNotFoundError:
256
- artifact_data = None
257
-
258
- filename = f"tasks_{datetime.now().strftime('%Y%m%d_%H%M%S')}"
259
- files = []
260
- if data_content:
261
- self.artifact.create(f'{filename}.json', data_content)
262
- files.append(f'{filename}.json')
263
-
264
- if artifact_data:
265
- self.artifact.create(f'{filename}.gif', artifact_data)
266
- files.append(f'{filename}.gif')
267
- return files
268
-
269
-
270
- def tasks(self, tasks: List[str], max_steps: Optional[int] = 20, debug: Optional[bool] = False):
271
- """Perform a list of tasks using the browser."""
272
- return asyncio.run(self._tasks(tasks, max_steps, debug))
273
-
274
- def get_available_tools(self):
275
- return [
276
- {
277
- "name": "task",
278
- "description": self.task.__doc__,
279
- "args_schema": BrowserTask,
280
- "ref": self.task
281
- },
282
- {
283
- "name": "tasks",
284
- "description": self.tasks.__doc__,
285
- "args_schema": BrowserTasks,
286
- "ref": self.tasks
287
- }
288
- ]
@@ -1,70 +0,0 @@
1
- from .deep_research import DeepResearcher
2
- from .iterative_research import IterativeResearcher
3
- from .agents.baseclass import ResearchRunner
4
- from .llm_config import LLMConfig
5
-
6
- __all__ = ["DeepResearcher", "IterativeResearcher", "ResearchRunner", "LLMConfig"]
7
-
8
- from typing import Any, List, Literal, Optional
9
-
10
- from langchain_core.tools import BaseToolkit, BaseTool
11
- from pydantic import BaseModel, ConfigDict, create_model, Field
12
-
13
- from .api_wrapper import DeepResearcherWrapper
14
- from ..base.tool import BaseAction
15
- from ..utils import clean_string, TOOLKIT_SPLITTER, get_max_toolkit_length
16
-
17
- name = "deep_researcher"
18
-
19
- def get_tools(tool):
20
- return DeepResearcherToolkit().get_toolkit(
21
- selected_tools=tool['settings'].get('selected_tools', []),
22
- max_iterations=tool['settings'].get('max_iterations', 5),
23
- max_time_minutes=tool['settings'].get('max_time_minutes', 10),
24
- verbose=tool['settings'].get('verbose', False),
25
- tracing=tool['settings'].get('tracing', False),
26
- alita=tool['settings'].get('alita', None),
27
- llm=tool['settings'].get('llm', None),
28
- toolkit_name=tool.get('toolkit_name')
29
- ).get_tools()
30
-
31
-
32
- class DeepResearcherToolkit(BaseToolkit):
33
- tools: List[BaseTool] = []
34
- toolkit_max_length: int = 0
35
-
36
- @staticmethod
37
- def toolkit_config_schema() -> BaseModel:
38
- selected_tools = {x['name']: x['args_schema'].schema() for x in DeepResearcherWrapper.model_construct().get_available_tools()}
39
- DeepResearcherToolkit.toolkit_max_length = get_max_toolkit_length(selected_tools)
40
- return create_model(
41
- name,
42
- max_iterations=(int, Field(default=5, title="Max iterations", description="Maximum number of iterations for research", json_schema_extra={'toolkit_name': True, 'max_toolkit_length': DeepResearcherToolkit.toolkit_max_length})),
43
- max_time_minutes=(int, Field(default=10, title="Max time (minutes)", description="Maximum time in minutes for research")),
44
- verbose=(bool, Field(default=False, title="Verbose", description="Print status updates to the console")),
45
- tracing=(bool, Field(default=False, title="Tracing", description="Enable tracing (only for OpenAI models)")),
46
- selected_tools=(List[Literal[tuple(selected_tools)]], Field(default=[], json_schema_extra={'args_schemas': selected_tools})),
47
- __config__=ConfigDict(json_schema_extra={'metadata': {"label": "Deep Researcher", "icon_url": "research-icon.svg"}})
48
- )
49
-
50
- @classmethod
51
- def get_toolkit(cls, selected_tools: list[str] | None = None, toolkit_name: Optional[str] = None, **kwargs):
52
- if selected_tools is None:
53
- selected_tools = []
54
- deep_researcher_api_wrapper = DeepResearcherWrapper(**kwargs)
55
- prefix = clean_string(toolkit_name, cls.toolkit_max_length) + TOOLKIT_SPLITTER if toolkit_name else ''
56
- available_tools = deep_researcher_api_wrapper.get_available_tools()
57
- tools = []
58
- for tool in available_tools:
59
- if selected_tools and tool["name"] not in selected_tools:
60
- continue
61
- tools.append(BaseAction(
62
- api_wrapper=deep_researcher_api_wrapper,
63
- name=prefix + tool["name"],
64
- description=tool["description"],
65
- args_schema=tool["args_schema"]
66
- ))
67
- return cls(tools=tools)
68
-
69
- def get_tools(self):
70
- return self.tools
@@ -1 +0,0 @@
1
- from .baseclass import ResearchAgent, ResearchRunner
@@ -1,182 +0,0 @@
1
- from typing import Any, Callable, Optional, List, Dict, Union, TypeVar, Generic, Type
2
- from pydantic import BaseModel
3
- import asyncio
4
- import json
5
-
6
- # LangChain imports
7
- from langchain_core.tools import BaseTool
8
- from langchain_core.messages import AIMessage, HumanMessage, SystemMessage, BaseMessage
9
- from langchain_core.prompts import ChatPromptTemplate
10
- from langchain.agents import AgentExecutor
11
- from langchain_core.runnables import RunnablePassthrough
12
- from langchain.agents.format_scratchpad import format_to_openai_functions
13
- from langchain.agents.output_parsers import OpenAIFunctionsAgentOutputParser
14
- from langchain_core.runnables.base import RunnableSerializable
15
-
16
- # Type variable for parameterizing the output type
17
- TContext = TypeVar("TContext")
18
-
19
- class ResearchRunner:
20
- """
21
- LangChain-based runner for research agents that supports both structured output
22
- and custom output parsing.
23
- """
24
-
25
- @classmethod
26
- async def run(cls, agent, user_message: str, **kwargs) -> 'RunResult':
27
- """
28
- Run the agent with the given user message and return the result.
29
-
30
- Args:
31
- agent: The agent to run
32
- user_message: The user message to send to the agent
33
-
34
- Returns:
35
- A RunResult containing the final output
36
- """
37
- if not isinstance(agent, ResearchAgent):
38
- raise TypeError("Agent must be a ResearchAgent")
39
-
40
- result = await agent.arun(user_message)
41
- return RunResult(final_output=result)
42
-
43
- class RunResult:
44
- """
45
- A simple class to maintain compatibility with the previous API
46
- while using LangChain agents under the hood.
47
- """
48
-
49
- def __init__(self, final_output: Any):
50
- self.final_output = final_output
51
-
52
- def final_output_as(self, output_type: Type[Any]) -> Any:
53
- """
54
- Convert the final output to the specified type.
55
-
56
- Args:
57
- output_type: The type to convert to
58
-
59
- Returns:
60
- An instance of output_type
61
- """
62
- if isinstance(self.final_output, output_type):
63
- return self.final_output
64
-
65
- if isinstance(self.final_output, str):
66
- try:
67
- # Try to parse as JSON if it's a string
68
- parsed = json.loads(self.final_output)
69
- return output_type(**parsed)
70
- except Exception:
71
- # If that fails, try to parse the string for JSON
72
- try:
73
- # Look for JSON-like content in the string
74
- import re
75
- json_match = re.search(r'```json\n(.*?)\n```', self.final_output, re.DOTALL)
76
- if json_match:
77
- json_str = json_match.group(1)
78
- parsed = json.loads(json_str)
79
- return output_type(**parsed)
80
- except Exception:
81
- pass
82
-
83
- # If all else fails, try to initialize with the entire output as a string
84
- try:
85
- if hasattr(output_type, "model_validate"):
86
- return output_type.model_validate({"output": self.final_output})
87
- else:
88
- return output_type(output=self.final_output)
89
- except Exception as e:
90
- raise ValueError(f"Could not convert output to {output_type.__name__}: {e}")
91
-
92
- class ResearchAgent(Generic[TContext]):
93
- """
94
- LangChain-based agent for research tasks that supports both structured output
95
- and custom output parsing.
96
- """
97
-
98
- def __init__(
99
- self,
100
- name: str,
101
- instructions: str,
102
- tools: List[BaseTool],
103
- model: Any,
104
- output_type: Optional[Type[BaseModel]] = None,
105
- output_parser: Optional[Callable[[str], Any]] = None
106
- ):
107
- self.name = name
108
- self.instructions = instructions
109
- self.tools = tools
110
- self.model = model
111
- self.output_type = output_type
112
- self.output_parser = output_parser
113
-
114
- # Create the LangChain agent
115
- self.agent = self._create_agent()
116
-
117
- def _create_agent(self) -> RunnableSerializable:
118
- """
119
- Create a LangChain agent with the specified configuration.
120
- """
121
- # Create the system prompt
122
- system_prompt = self.instructions
123
-
124
- # Create the prompt template
125
- prompt = ChatPromptTemplate.from_messages([
126
- ("system", system_prompt),
127
- ("human", "{input}"),
128
- ("ai", "{agent_scratchpad}")
129
- ])
130
-
131
- # Create the LangChain agent
132
- agent = (
133
- {
134
- "input": RunnablePassthrough(),
135
- "agent_scratchpad": lambda x: format_to_openai_functions(x["intermediate_steps"])
136
- }
137
- | prompt
138
- | self.model
139
- | OpenAIFunctionsAgentOutputParser()
140
- )
141
-
142
- # Create the agent executor
143
- return AgentExecutor(
144
- agent=agent,
145
- tools=self.tools,
146
- verbose=True,
147
- handle_parsing_errors=True
148
- )
149
-
150
- async def arun(self, user_input: str) -> Any:
151
- """
152
- Run the agent asynchronously with the given user input.
153
-
154
- Args:
155
- user_input: The user input to send to the agent
156
-
157
- Returns:
158
- The agent's output
159
- """
160
- try:
161
- # Run the agent
162
- result = await self.agent.ainvoke({"input": user_input, "intermediate_steps": []})
163
- output = result.get("output", "")
164
-
165
- # Apply output parser if specified
166
- if self.output_parser is not None:
167
- return self.output_parser(output)
168
-
169
- # Try to convert to output_type if specified
170
- if self.output_type is not None:
171
- try:
172
- return self.output_type.model_validate_json(output)
173
- except Exception:
174
- try:
175
- return self.output_type.model_validate({"output": output})
176
- except Exception:
177
- pass
178
-
179
- # Otherwise return the raw output
180
- return output
181
- except Exception as e:
182
- return f"Error: {str(e)}"
@@ -1,74 +0,0 @@
1
- """
2
- Agent used to evaluate the state of the research report (typically done in a loop) and identify knowledge gaps that still
3
- need to be addressed.
4
-
5
- The Agent takes as input a string in the following format:
6
- ===========================================================
7
- ORIGINAL QUERY: <original user query>
8
-
9
- HISTORY OF ACTIONS, FINDINGS AND THOUGHTS: <breakdown of activities and findings carried out so far>
10
- ===========================================================
11
-
12
- The Agent then:
13
- 1. Carefully reviews the current draft and assesses its completeness in answering the original query
14
- 2. Identifies specific knowledge gaps that still exist and need to be filled
15
- 3. Returns a KnowledgeGapOutput object
16
- """
17
-
18
- from pydantic import BaseModel, Field
19
- from typing import List, Optional, Any
20
- from langchain_core.tools import BaseTool
21
- from .baseclass import ResearchAgent
22
- from ..llm_config import LLMConfig, model_supports_structured_output
23
- from datetime import datetime
24
- from .utils.parse_output import create_type_parser
25
-
26
- class KnowledgeGapOutput(BaseModel):
27
- """Output from the Knowledge Gap Agent"""
28
- research_complete: bool = Field(description="Whether the research and findings are complete enough to end the research loop")
29
- outstanding_gaps: List[str] = Field(description="List of knowledge gaps that still need to be addressed")
30
-
31
-
32
- INSTRUCTIONS = f"""
33
- You are a Research State Evaluator. Today's date is {datetime.now().strftime("%Y-%m-%d")}.
34
- Your job is to critically analyze the current state of a research report,
35
- identify what knowledge gaps still exist and determine the best next step to take.
36
-
37
- You will be given:
38
- 1. The original user query and any relevant background context to the query
39
- 2. A full history of the tasks, actions, findings and thoughts you've made up until this point in the research process
40
-
41
- Your task is to:
42
- 1. Carefully review the findings and thoughts, particularly from the latest iteration, and assess their completeness in answering the original query
43
- 2. Determine if the findings are sufficiently complete to end the research loop
44
- 3. If not, identify up to 3 knowledge gaps that need to be addressed in sequence in order to continue with research - these should be relevant to the original query
45
-
46
- Be specific in the gaps you identify and include relevant information as this will be passed onto another agent to process without additional context.
47
-
48
- Only output JSON. Follow the JSON schema below. Do not output anything else. I will be parsing this with Pydantic so output valid JSON only:
49
- {KnowledgeGapOutput.model_json_schema()}
50
- """
51
-
52
- def init_knowledge_gap_agent(config: LLMConfig) -> ResearchAgent:
53
- """
54
- Initialize the knowledge gap agent.
55
-
56
- Args:
57
- config: The LLM configuration to use
58
-
59
- Returns:
60
- A ResearchAgent that can evaluate knowledge gaps
61
- """
62
- selected_model = config.fast_model
63
-
64
- # Determine whether to use structured output based on if we have a Langchain LLM
65
- use_output_parser = not hasattr(selected_model, 'langchain_llm')
66
-
67
- return ResearchAgent(
68
- name="KnowledgeGapAgent",
69
- instructions=INSTRUCTIONS,
70
- tools=[], # No tools needed for this agent
71
- model=selected_model.langchain_llm if hasattr(selected_model, 'langchain_llm') else selected_model,
72
- output_type=KnowledgeGapOutput if not use_output_parser else None,
73
- output_parser=create_type_parser(KnowledgeGapOutput) if use_output_parser else None
74
- )