lfx-nightly 0.1.13.dev0__py3-none-any.whl → 0.2.0.dev26__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 (237) hide show
  1. lfx/_assets/component_index.json +1 -1
  2. lfx/base/agents/agent.py +121 -29
  3. lfx/base/agents/altk_base_agent.py +380 -0
  4. lfx/base/agents/altk_tool_wrappers.py +565 -0
  5. lfx/base/agents/events.py +103 -35
  6. lfx/base/agents/utils.py +15 -2
  7. lfx/base/composio/composio_base.py +183 -233
  8. lfx/base/data/base_file.py +88 -21
  9. lfx/base/data/storage_utils.py +192 -0
  10. lfx/base/data/utils.py +178 -14
  11. lfx/base/datastax/__init__.py +5 -0
  12. lfx/{components/vectorstores/astradb.py → base/datastax/astradb_base.py} +84 -473
  13. lfx/base/embeddings/embeddings_class.py +113 -0
  14. lfx/base/io/chat.py +5 -4
  15. lfx/base/mcp/util.py +101 -15
  16. lfx/base/models/groq_constants.py +74 -58
  17. lfx/base/models/groq_model_discovery.py +265 -0
  18. lfx/base/models/model.py +1 -1
  19. lfx/base/models/model_input_constants.py +74 -7
  20. lfx/base/models/model_utils.py +100 -0
  21. lfx/base/models/ollama_constants.py +3 -0
  22. lfx/base/models/openai_constants.py +7 -0
  23. lfx/base/models/watsonx_constants.py +36 -0
  24. lfx/base/tools/run_flow.py +601 -129
  25. lfx/cli/commands.py +7 -4
  26. lfx/cli/common.py +2 -2
  27. lfx/cli/run.py +1 -1
  28. lfx/cli/script_loader.py +53 -11
  29. lfx/components/Notion/create_page.py +1 -1
  30. lfx/components/Notion/list_database_properties.py +1 -1
  31. lfx/components/Notion/list_pages.py +1 -1
  32. lfx/components/Notion/list_users.py +1 -1
  33. lfx/components/Notion/page_content_viewer.py +1 -1
  34. lfx/components/Notion/search.py +1 -1
  35. lfx/components/Notion/update_page_property.py +1 -1
  36. lfx/components/__init__.py +19 -5
  37. lfx/components/altk/__init__.py +34 -0
  38. lfx/components/altk/altk_agent.py +193 -0
  39. lfx/components/amazon/amazon_bedrock_converse.py +1 -1
  40. lfx/components/apify/apify_actor.py +4 -4
  41. lfx/components/composio/__init__.py +70 -18
  42. lfx/components/composio/apollo_composio.py +11 -0
  43. lfx/components/composio/bitbucket_composio.py +11 -0
  44. lfx/components/composio/canva_composio.py +11 -0
  45. lfx/components/composio/coda_composio.py +11 -0
  46. lfx/components/composio/composio_api.py +10 -0
  47. lfx/components/composio/discord_composio.py +1 -1
  48. lfx/components/composio/elevenlabs_composio.py +11 -0
  49. lfx/components/composio/exa_composio.py +11 -0
  50. lfx/components/composio/firecrawl_composio.py +11 -0
  51. lfx/components/composio/fireflies_composio.py +11 -0
  52. lfx/components/composio/gmail_composio.py +1 -1
  53. lfx/components/composio/googlebigquery_composio.py +11 -0
  54. lfx/components/composio/googlecalendar_composio.py +1 -1
  55. lfx/components/composio/googledocs_composio.py +1 -1
  56. lfx/components/composio/googlemeet_composio.py +1 -1
  57. lfx/components/composio/googlesheets_composio.py +1 -1
  58. lfx/components/composio/googletasks_composio.py +1 -1
  59. lfx/components/composio/heygen_composio.py +11 -0
  60. lfx/components/composio/mem0_composio.py +11 -0
  61. lfx/components/composio/peopledatalabs_composio.py +11 -0
  62. lfx/components/composio/perplexityai_composio.py +11 -0
  63. lfx/components/composio/serpapi_composio.py +11 -0
  64. lfx/components/composio/slack_composio.py +3 -574
  65. lfx/components/composio/slackbot_composio.py +1 -1
  66. lfx/components/composio/snowflake_composio.py +11 -0
  67. lfx/components/composio/tavily_composio.py +11 -0
  68. lfx/components/composio/youtube_composio.py +2 -2
  69. lfx/components/{agents → cuga}/__init__.py +5 -7
  70. lfx/components/cuga/cuga_agent.py +730 -0
  71. lfx/components/data/__init__.py +78 -28
  72. lfx/components/data_source/__init__.py +58 -0
  73. lfx/components/{data → data_source}/api_request.py +26 -3
  74. lfx/components/{data → data_source}/csv_to_data.py +15 -10
  75. lfx/components/{data → data_source}/json_to_data.py +15 -8
  76. lfx/components/{data → data_source}/news_search.py +1 -1
  77. lfx/components/{data → data_source}/rss.py +1 -1
  78. lfx/components/{data → data_source}/sql_executor.py +1 -1
  79. lfx/components/{data → data_source}/url.py +1 -1
  80. lfx/components/{data → data_source}/web_search.py +1 -1
  81. lfx/components/datastax/__init__.py +12 -6
  82. lfx/components/datastax/{astra_assistant_manager.py → astradb_assistant_manager.py} +1 -0
  83. lfx/components/datastax/astradb_chatmemory.py +40 -0
  84. lfx/components/datastax/astradb_cql.py +6 -32
  85. lfx/components/datastax/astradb_graph.py +10 -124
  86. lfx/components/datastax/astradb_tool.py +13 -53
  87. lfx/components/datastax/astradb_vectorstore.py +134 -977
  88. lfx/components/datastax/create_assistant.py +1 -0
  89. lfx/components/datastax/create_thread.py +1 -0
  90. lfx/components/datastax/dotenv.py +1 -0
  91. lfx/components/datastax/get_assistant.py +1 -0
  92. lfx/components/datastax/getenvvar.py +1 -0
  93. lfx/components/datastax/graph_rag.py +1 -1
  94. lfx/components/datastax/hcd.py +1 -1
  95. lfx/components/datastax/list_assistants.py +1 -0
  96. lfx/components/datastax/run.py +1 -0
  97. lfx/components/deactivated/json_document_builder.py +1 -1
  98. lfx/components/elastic/elasticsearch.py +1 -1
  99. lfx/components/elastic/opensearch_multimodal.py +1575 -0
  100. lfx/components/files_and_knowledge/__init__.py +47 -0
  101. lfx/components/{data → files_and_knowledge}/directory.py +1 -1
  102. lfx/components/{data → files_and_knowledge}/file.py +246 -18
  103. lfx/components/{knowledge_bases → files_and_knowledge}/ingestion.py +17 -9
  104. lfx/components/{knowledge_bases → files_and_knowledge}/retrieval.py +18 -10
  105. lfx/components/{data → files_and_knowledge}/save_file.py +142 -22
  106. lfx/components/flow_controls/__init__.py +58 -0
  107. lfx/components/{logic → flow_controls}/conditional_router.py +1 -1
  108. lfx/components/{logic → flow_controls}/loop.py +47 -9
  109. lfx/components/flow_controls/run_flow.py +108 -0
  110. lfx/components/glean/glean_search_api.py +1 -1
  111. lfx/components/groq/groq.py +35 -28
  112. lfx/components/helpers/__init__.py +102 -0
  113. lfx/components/ibm/watsonx.py +25 -21
  114. lfx/components/input_output/__init__.py +3 -1
  115. lfx/components/input_output/chat.py +12 -3
  116. lfx/components/input_output/chat_output.py +12 -4
  117. lfx/components/input_output/text.py +1 -1
  118. lfx/components/input_output/text_output.py +1 -1
  119. lfx/components/{data → input_output}/webhook.py +1 -1
  120. lfx/components/knowledge_bases/__init__.py +59 -4
  121. lfx/components/langchain_utilities/character.py +1 -1
  122. lfx/components/langchain_utilities/csv_agent.py +84 -16
  123. lfx/components/langchain_utilities/json_agent.py +67 -12
  124. lfx/components/langchain_utilities/language_recursive.py +1 -1
  125. lfx/components/llm_operations/__init__.py +46 -0
  126. lfx/components/{processing → llm_operations}/batch_run.py +1 -1
  127. lfx/components/{processing → llm_operations}/lambda_filter.py +1 -1
  128. lfx/components/{logic → llm_operations}/llm_conditional_router.py +1 -1
  129. lfx/components/{processing/llm_router.py → llm_operations/llm_selector.py} +3 -3
  130. lfx/components/{processing → llm_operations}/structured_output.py +56 -18
  131. lfx/components/logic/__init__.py +126 -0
  132. lfx/components/mem0/mem0_chat_memory.py +11 -0
  133. lfx/components/mistral/mistral_embeddings.py +1 -1
  134. lfx/components/models/__init__.py +64 -9
  135. lfx/components/models_and_agents/__init__.py +49 -0
  136. lfx/components/{agents → models_and_agents}/agent.py +49 -6
  137. lfx/components/models_and_agents/embedding_model.py +423 -0
  138. lfx/components/models_and_agents/language_model.py +398 -0
  139. lfx/components/{agents → models_and_agents}/mcp_component.py +84 -45
  140. lfx/components/{helpers → models_and_agents}/memory.py +1 -1
  141. lfx/components/nvidia/system_assist.py +1 -1
  142. lfx/components/olivya/olivya.py +1 -1
  143. lfx/components/ollama/ollama.py +235 -14
  144. lfx/components/openrouter/openrouter.py +49 -147
  145. lfx/components/processing/__init__.py +9 -57
  146. lfx/components/processing/converter.py +1 -1
  147. lfx/components/processing/dataframe_operations.py +1 -1
  148. lfx/components/processing/parse_json_data.py +2 -2
  149. lfx/components/processing/parser.py +7 -2
  150. lfx/components/processing/split_text.py +1 -1
  151. lfx/components/qdrant/qdrant.py +1 -1
  152. lfx/components/redis/redis.py +1 -1
  153. lfx/components/twelvelabs/split_video.py +10 -0
  154. lfx/components/twelvelabs/video_file.py +12 -0
  155. lfx/components/utilities/__init__.py +43 -0
  156. lfx/components/{helpers → utilities}/calculator_core.py +1 -1
  157. lfx/components/{helpers → utilities}/current_date.py +1 -1
  158. lfx/components/{processing → utilities}/python_repl_core.py +1 -1
  159. lfx/components/vectorstores/__init__.py +0 -6
  160. lfx/components/vectorstores/local_db.py +9 -0
  161. lfx/components/youtube/youtube_transcripts.py +118 -30
  162. lfx/custom/custom_component/component.py +60 -3
  163. lfx/custom/custom_component/custom_component.py +68 -6
  164. lfx/field_typing/constants.py +1 -0
  165. lfx/graph/edge/base.py +45 -22
  166. lfx/graph/graph/base.py +5 -2
  167. lfx/graph/graph/schema.py +3 -2
  168. lfx/graph/state/model.py +15 -2
  169. lfx/graph/utils.py +6 -0
  170. lfx/graph/vertex/base.py +4 -1
  171. lfx/graph/vertex/param_handler.py +10 -7
  172. lfx/graph/vertex/vertex_types.py +1 -1
  173. lfx/helpers/__init__.py +12 -0
  174. lfx/helpers/flow.py +117 -0
  175. lfx/inputs/input_mixin.py +24 -1
  176. lfx/inputs/inputs.py +13 -1
  177. lfx/interface/components.py +161 -83
  178. lfx/io/schema.py +6 -0
  179. lfx/log/logger.py +5 -3
  180. lfx/schema/schema.py +5 -0
  181. lfx/services/database/__init__.py +5 -0
  182. lfx/services/database/service.py +25 -0
  183. lfx/services/deps.py +87 -22
  184. lfx/services/manager.py +19 -6
  185. lfx/services/mcp_composer/service.py +998 -157
  186. lfx/services/session.py +5 -0
  187. lfx/services/settings/base.py +51 -7
  188. lfx/services/settings/constants.py +8 -0
  189. lfx/services/storage/local.py +76 -46
  190. lfx/services/storage/service.py +152 -29
  191. lfx/template/field/base.py +3 -0
  192. lfx/utils/ssrf_protection.py +384 -0
  193. lfx/utils/validate_cloud.py +26 -0
  194. {lfx_nightly-0.1.13.dev0.dist-info → lfx_nightly-0.2.0.dev26.dist-info}/METADATA +38 -22
  195. {lfx_nightly-0.1.13.dev0.dist-info → lfx_nightly-0.2.0.dev26.dist-info}/RECORD +210 -196
  196. {lfx_nightly-0.1.13.dev0.dist-info → lfx_nightly-0.2.0.dev26.dist-info}/WHEEL +1 -1
  197. lfx/components/agents/cuga_agent.py +0 -1013
  198. lfx/components/datastax/astra_db.py +0 -77
  199. lfx/components/datastax/cassandra.py +0 -92
  200. lfx/components/logic/run_flow.py +0 -71
  201. lfx/components/models/embedding_model.py +0 -114
  202. lfx/components/models/language_model.py +0 -144
  203. lfx/components/vectorstores/astradb_graph.py +0 -326
  204. lfx/components/vectorstores/cassandra.py +0 -264
  205. lfx/components/vectorstores/cassandra_graph.py +0 -238
  206. lfx/components/vectorstores/chroma.py +0 -167
  207. lfx/components/vectorstores/clickhouse.py +0 -135
  208. lfx/components/vectorstores/couchbase.py +0 -102
  209. lfx/components/vectorstores/elasticsearch.py +0 -267
  210. lfx/components/vectorstores/faiss.py +0 -111
  211. lfx/components/vectorstores/graph_rag.py +0 -141
  212. lfx/components/vectorstores/hcd.py +0 -314
  213. lfx/components/vectorstores/milvus.py +0 -115
  214. lfx/components/vectorstores/mongodb_atlas.py +0 -213
  215. lfx/components/vectorstores/opensearch.py +0 -243
  216. lfx/components/vectorstores/pgvector.py +0 -72
  217. lfx/components/vectorstores/pinecone.py +0 -134
  218. lfx/components/vectorstores/qdrant.py +0 -109
  219. lfx/components/vectorstores/supabase.py +0 -76
  220. lfx/components/vectorstores/upstash.py +0 -124
  221. lfx/components/vectorstores/vectara.py +0 -97
  222. lfx/components/vectorstores/vectara_rag.py +0 -164
  223. lfx/components/vectorstores/weaviate.py +0 -89
  224. /lfx/components/{data → data_source}/mock_data.py +0 -0
  225. /lfx/components/datastax/{astra_vectorize.py → astradb_vectorize.py} +0 -0
  226. /lfx/components/{logic → flow_controls}/data_conditional_router.py +0 -0
  227. /lfx/components/{logic → flow_controls}/flow_tool.py +0 -0
  228. /lfx/components/{logic → flow_controls}/listen.py +0 -0
  229. /lfx/components/{logic → flow_controls}/notify.py +0 -0
  230. /lfx/components/{logic → flow_controls}/pass_message.py +0 -0
  231. /lfx/components/{logic → flow_controls}/sub_flow.py +0 -0
  232. /lfx/components/{processing → models_and_agents}/prompt.py +0 -0
  233. /lfx/components/{helpers → processing}/create_list.py +0 -0
  234. /lfx/components/{helpers → processing}/output_parser.py +0 -0
  235. /lfx/components/{helpers → processing}/store_message.py +0 -0
  236. /lfx/components/{helpers → utilities}/id_generator.py +0 -0
  237. {lfx_nightly-0.1.13.dev0.dist-info → lfx_nightly-0.2.0.dev26.dist-info}/entry_points.txt +0 -0
@@ -1,1013 +0,0 @@
1
- import asyncio
2
- import json
3
- import os
4
- import re
5
- import traceback
6
- import uuid
7
- from collections.abc import AsyncIterator
8
- from typing import TYPE_CHECKING, Any, cast
9
-
10
- from langchain_core.agents import AgentFinish
11
- from langchain_core.messages import AIMessage, HumanMessage
12
- from langchain_core.tools import StructuredTool
13
- from langflow.field_typing import Tool
14
- from langflow.io import BoolInput, DropdownInput, IntInput, MultilineInput, Output, TableInput
15
-
16
- # from langflow.logging import logger
17
- from langflow.schema.data import Data
18
- from langflow.schema.dotdict import dotdict
19
- from langflow.schema.message import Message
20
- from langflow.schema.table import EditMode
21
- from pydantic import ValidationError
22
-
23
- from lfx.base.agents.events import ExceptionWithMessageError
24
- from lfx.base.models.model_input_constants import (
25
- ALL_PROVIDER_FIELDS,
26
- MODEL_DYNAMIC_UPDATE_FIELDS,
27
- MODEL_PROVIDERS,
28
- MODEL_PROVIDERS_DICT,
29
- MODELS_METADATA,
30
- )
31
- from lfx.base.models.model_utils import get_model_name
32
- from lfx.components.agents.agent import LCToolsAgentComponent
33
- from lfx.components.helpers.current_date import CurrentDateComponent
34
- from lfx.components.helpers.memory import MemoryComponent
35
- from lfx.components.langchain_utilities.tool_calling import ToolCallingAgentComponent
36
- from lfx.custom.custom_component.component import _get_component_toolkit
37
- from lfx.custom.utils import update_component_build_config
38
- from lfx.helpers.base_model import build_model_from_schema
39
- from lfx.log.logger import logger
40
-
41
- if TYPE_CHECKING:
42
- from langflow.schema.log import SendMessageFunctionType
43
-
44
-
45
- def set_advanced_true(component_input):
46
- """Set the advanced flag to True for a component input.
47
-
48
- Args:
49
- component_input: The component input to modify
50
-
51
- Returns:
52
- The modified component input with advanced=True
53
- """
54
- component_input.advanced = True
55
- return component_input
56
-
57
-
58
- MODEL_PROVIDERS_LIST = ["OpenAI"]
59
-
60
-
61
- class CugaComponent(ToolCallingAgentComponent):
62
- """Cuga Agent Component for advanced AI task execution.
63
-
64
- The Cuga component is an advanced AI agent that can execute complex tasks using
65
- various tools, browser automation, and structured output generation. It supports
66
- custom policies, web applications, and API interactions.
67
-
68
- Attributes:
69
- display_name: Human-readable name for the component
70
- description: Brief description of the component's purpose
71
- documentation: URL to component documentation
72
- icon: Icon identifier for the UI
73
- beta: Whether the component is in beta status
74
- name: Internal component name
75
- """
76
-
77
- display_name: str = "Cuga"
78
- description: str = "Define the Cuga agent's policies, then assign it a task."
79
- documentation: str = "https://docs.langflow.org/agents"
80
- icon = "bot"
81
- beta = True
82
- name = "Cuga"
83
-
84
- memory_inputs = [set_advanced_true(component_input) for component_input in MemoryComponent().inputs]
85
-
86
- # Filter out json_mode from OpenAI inputs since we handle structured output differently
87
- openai_inputs_filtered = [
88
- input_field
89
- for input_field in MODEL_PROVIDERS_DICT["OpenAI"]["inputs"]
90
- if not (hasattr(input_field, "name") and input_field.name == "json_mode")
91
- ]
92
-
93
- inputs = [
94
- DropdownInput(
95
- name="agent_llm",
96
- display_name="Model Provider",
97
- info="The provider of the language model that the agent will use to generate responses.",
98
- options=[*MODEL_PROVIDERS_LIST, "Custom"],
99
- value="OpenAI",
100
- real_time_refresh=True,
101
- input_types=[],
102
- options_metadata=[MODELS_METADATA[key] for key in MODEL_PROVIDERS_LIST] + [{"icon": "brain"}],
103
- ),
104
- *openai_inputs_filtered,
105
- MultilineInput(
106
- name="policies",
107
- display_name="Policies",
108
- info=(
109
- "Custom instructions or policies for the agent to adhere to during its operation.\n"
110
- "Example:\n"
111
- "# Plan\n"
112
- "< planning instructions e.g. which tools and when to use>\n"
113
- "# Answer\n"
114
- "< final answer instructions how to answer>"
115
- ),
116
- value="",
117
- advanced=False,
118
- ),
119
- IntInput(
120
- name="n_messages",
121
- display_name="Number of Chat History Messages",
122
- value=100,
123
- info="Number of chat history messages to retrieve.",
124
- advanced=True,
125
- show=True,
126
- ),
127
- MultilineInput(
128
- name="format_instructions",
129
- display_name="Output Format Instructions",
130
- info="Generic Template for structured output formatting. Valid only with Structured response.",
131
- value=(
132
- "You are an AI that extracts structured JSON objects from unstructured text. "
133
- "Use a predefined schema with expected types (str, int, float, bool, dict). "
134
- "Extract ALL relevant instances that match the schema - if multiple patterns exist, capture them all. "
135
- "Fill missing or ambiguous values with defaults: null for missing values. "
136
- "Remove exact duplicates but keep variations that have different field values. "
137
- "Always return valid JSON in the expected format, never throw errors. "
138
- "If multiple objects can be extracted, return them all in the structured format."
139
- ),
140
- advanced=True,
141
- ),
142
- TableInput(
143
- name="output_schema",
144
- display_name="Output Schema",
145
- info=(
146
- "Schema Validation: Define the structure and data types for structured output. "
147
- "No validation if no output schema."
148
- ),
149
- advanced=True,
150
- required=False,
151
- value=[],
152
- table_schema=[
153
- {
154
- "name": "name",
155
- "display_name": "Name",
156
- "type": "str",
157
- "description": "Specify the name of the output field.",
158
- "default": "field",
159
- "edit_mode": EditMode.INLINE,
160
- },
161
- {
162
- "name": "description",
163
- "display_name": "Description",
164
- "type": "str",
165
- "description": "Describe the purpose of the output field.",
166
- "default": "description of field",
167
- "edit_mode": EditMode.POPOVER,
168
- },
169
- {
170
- "name": "type",
171
- "display_name": "Type",
172
- "type": "str",
173
- "edit_mode": EditMode.INLINE,
174
- "description": ("Indicate the data type of the output field (e.g., str, int, float, bool, dict)."),
175
- "options": ["str", "int", "float", "bool", "dict"],
176
- "default": "str",
177
- },
178
- {
179
- "name": "multiple",
180
- "display_name": "As List",
181
- "type": "boolean",
182
- "description": "Set to True if this output field should be a list of the specified type.",
183
- "default": "False",
184
- "edit_mode": EditMode.INLINE,
185
- },
186
- ],
187
- ),
188
- *LCToolsAgentComponent._base_inputs,
189
- BoolInput(
190
- name="add_current_date_tool",
191
- display_name="Current Date",
192
- advanced=True,
193
- info="If true, will add a tool to the agent that returns the current date.",
194
- value=True,
195
- ),
196
- BoolInput(
197
- name="browser_enabled",
198
- display_name="Enable Browser",
199
- info="Toggle to enable a built-in browser tool for web scraping and searching.",
200
- value=False,
201
- advanced=False,
202
- ),
203
- MultilineInput(
204
- name="web_apps",
205
- display_name="Web applications",
206
- info=(
207
- "Define a list of web applications that cuga will open when enable browser is true. "
208
- "Currently only supports one web application. Example: https://example.com"
209
- ),
210
- value="",
211
- advanced=False,
212
- ),
213
- BoolInput(
214
- name="API",
215
- display_name="Enable API Sub-agent",
216
- info="Toggle to enable a built-in sub-agent specialized for API interactions.",
217
- value=False,
218
- advanced=True,
219
- ),
220
- ]
221
- outputs = [
222
- Output(name="response", display_name="Response", method="message_response"),
223
- Output(name="structured_response", display_name="Structured Response", method="json_response", tool_mode=False),
224
- ]
225
-
226
- async def call_agent(
227
- self, current_input: str, tools: list[Tool], history_messages: list[Message], llm
228
- ) -> AsyncIterator[dict[str, Any]]:
229
- """Execute the Cuga agent with the given input and tools.
230
-
231
- This method initializes and runs the Cuga agent, processing the input through
232
- the agent's workflow and yielding events for real-time monitoring.
233
-
234
- Args:
235
- current_input: The user input to process
236
- tools: List of available tools for the agent
237
- history_messages: Previous conversation history
238
- llm: The language model instance to use
239
-
240
- Yields:
241
- dict: Agent events including tool usage, thinking, and final results
242
-
243
- Raises:
244
- ValueError: If there's an error in agent initialization
245
- TypeError: If there's a type error in processing
246
- RuntimeError: If there's a runtime error during execution
247
- ConnectionError: If there's a connection issue
248
- """
249
- yield {
250
- "event": "on_chain_start",
251
- "run_id": str(uuid.uuid4()),
252
- "name": "CUGA_initializing",
253
- "data": {"input": {"input": current_input, "chat_history": []}},
254
- }
255
- logger.debug(f"LLM MODEL TYPE: {type(llm)}")
256
- if current_input:
257
- try:
258
- from cuga.config import settings as cuga_settings
259
-
260
- logger.info("Updating cuga settings programmatically")
261
- cuga_settings.set("advanced_features.registry", False) # noqa: FBT003
262
-
263
- if self.browser_enabled:
264
- logger.info("browser_enabled is true, setting mode to hybrid")
265
- cuga_settings.set("advanced_features.mode", "hybrid")
266
- cuga_settings.set("advanced_features.use_vision", False) # noqa: FBT003
267
- else:
268
- logger.info("browser_enabled is false, setting mode to api")
269
- cuga_settings.set("advanced_features.mode", "api")
270
-
271
- logger.info(f"Cuga settings updated - MODE: {cuga_settings.get('advanced_features.mode')}")
272
- except (ImportError, AttributeError) as e:
273
- logger.warning(f"Could not update cuga settings: {e}")
274
- os.environ["DYNACONF_ADVANCED_FEATURES__REGISTRY"] = "false"
275
- if self.browser_enabled:
276
- logger.info("browser_enabled is true, setting env to hybrid")
277
- os.environ["DYNACONF_ADVANCED_FEATURES__MODE"] = "hybrid"
278
- os.environ["DYNACONF_ADVANCED_FEATURES__USE_VISION"] = "false"
279
- else:
280
- logger.info("browser_enabled is false, setting env to api")
281
- os.environ["DYNACONF_ADVANCED_FEATURES__MODE"] = "api"
282
-
283
- from cuga.backend.activity_tracker.tracker import ActivityTracker
284
- from cuga.backend.cuga_graph.utils.agent_loop import StreamEvent
285
- from cuga.backend.cuga_graph.utils.controller import (
286
- AgentRunner as CugaAgent,
287
- )
288
- from cuga.backend.cuga_graph.utils.controller import (
289
- ExperimentResult as AgentResult,
290
- )
291
- from cuga.backend.llm.models import LLMManager
292
- from cuga.configurations.instructions_manager import InstructionsManager
293
-
294
- llm_manager = LLMManager()
295
- llm_manager.set_llm(llm)
296
- instructions_manager = InstructionsManager()
297
- if self.policies:
298
- logger.info(f"policies are: {self.policies}")
299
- instructions_manager.set_instructions_from_one_file(self.policies)
300
- tracker = ActivityTracker()
301
- tracker.set_tools(tools)
302
- cuga_agent = CugaAgent(browser_enabled=self.browser_enabled)
303
- if self.browser_enabled:
304
- await cuga_agent.initialize_freemode_env(start_url=self.web_apps.strip(), interface_mode="browser_only")
305
- else:
306
- await cuga_agent.initialize_appworld_env()
307
- yield {
308
- "event": "on_chain_start",
309
- "run_id": str(uuid.uuid4()),
310
- "name": "CUGA_thinking...",
311
- "data": {"input": {"input": current_input, "chat_history": []}},
312
- }
313
- logger.info(f"[CUGA] current web apps are {self.web_apps}")
314
- logger.info(f"[CUGA] Processing input: {current_input}")
315
- try:
316
- # Convert history to LangChain format for the event
317
- lc_messages = []
318
- for msg in history_messages:
319
- if hasattr(msg, "sender") and msg.sender == "Human":
320
- lc_messages.append(HumanMessage(content=msg.text))
321
- else:
322
- lc_messages.append(AIMessage(content=msg.text))
323
-
324
- await asyncio.sleep(0.5)
325
-
326
- tools_used = []
327
-
328
- # Simulate browser tool usage
329
- if getattr(self, "BROWSER", False) and any(
330
- word in current_input.lower() for word in ["search", "web", "browse"]
331
- ):
332
- tool_run_id = str(uuid.uuid4())
333
-
334
- yield {
335
- "event": "on_tool_start",
336
- "run_id": tool_run_id,
337
- "name": "BrowserTool",
338
- "data": {"input": {"query": current_input}},
339
- }
340
- await asyncio.sleep(0.3)
341
-
342
- yield {
343
- "event": "on_tool_end",
344
- "run_id": tool_run_id,
345
- "name": "BrowserTool",
346
- "data": {"output": "Simulated web search results for: " + current_input},
347
- }
348
- tools_used.append("Performed web search")
349
-
350
- # 2. Build final response
351
- response_parts = []
352
-
353
- response_parts.append(f"Processed input: '{current_input}'")
354
- response_parts.append(f"Available tools: {len(tools)}")
355
- # final_response = "CUGA Agent Response:\n" + "\n".join(response_parts)
356
- last_event: StreamEvent = None
357
- tool_run_id = None
358
- # 3. Chain end event with AgentFinish
359
- async for event in cuga_agent.run_task_generic_yield(eval_mode=False, goal=current_input):
360
- logger.debug(f"recieved event {event}")
361
- if last_event is not None and tool_run_id is not None:
362
- logger.debug(f"last event {last_event}")
363
- try:
364
- # TODO: Extract data
365
- data_dict = json.loads(last_event.data)
366
- except json.JSONDecodeError:
367
- data_dict = last_event.data
368
- if last_event.name == "CodeAgent":
369
- data_dict = data_dict["code"]
370
- yield {
371
- "event": "on_tool_end",
372
- "run_id": tool_run_id,
373
- "name": last_event.name,
374
- "data": {"output": data_dict},
375
- }
376
- if isinstance(event, StreamEvent):
377
- tool_run_id = str(uuid.uuid4())
378
- last_event = StreamEvent(name=event.name, data=event.data)
379
- tool_event = {
380
- "event": "on_tool_start",
381
- "run_id": tool_run_id,
382
- "name": event.name,
383
- "data": {"input": {}},
384
- }
385
- logger.debug(f"[CUGA] Yielding tool_start event: {event.name}")
386
- yield tool_event
387
-
388
- if isinstance(event, AgentResult):
389
- task_result = event
390
- end_event = {
391
- "event": "on_chain_end",
392
- "run_id": str(uuid.uuid4()),
393
- "name": "CugaAgent",
394
- "data": {"output": AgentFinish(return_values={"output": task_result.answer}, log="")},
395
- }
396
- answer_preview = task_result.answer[:100] if task_result.answer else "None"
397
- logger.info(f"[CUGA] Yielding chain_end event with answer: {answer_preview}...")
398
- yield end_event
399
- # task_result: AgentResult = await cuga_agent.run_task_generic_yield(
400
- # eval_mode=False, goal=current_input, on_progress=on_progress
401
- # )
402
-
403
- except (ValueError, TypeError, RuntimeError, ConnectionError) as e:
404
- logger.error(f"An error occurred: {e!s}")
405
- logger.error(f"Traceback: {traceback.format_exc()}")
406
- error_msg = f"CUGA Agent error: {e!s}"
407
- logger.error(f"[CUGA] Error occurred: {error_msg}")
408
-
409
- # Emit error event
410
- yield {
411
- "event": "on_chain_error",
412
- "run_id": str(uuid.uuid4()),
413
- "name": "CugaAgent",
414
- "data": {"error": error_msg},
415
- }
416
-
417
- async def message_response(self) -> Message:
418
- """Generate a message response using the Cuga agent.
419
-
420
- This method processes the input through the Cuga agent and returns a structured
421
- message response. It handles agent initialization, tool setup, and event processing.
422
-
423
- Returns:
424
- Message: The agent's response message
425
-
426
- Raises:
427
- Exception: If there's an error during agent execution
428
- """
429
- logger.info("[CUGA] Starting Cuga agent run for message_response.")
430
- logger.info(f"[CUGA] Agent input value: {self.input_value}")
431
-
432
- # Validate input is not empty
433
- if not self.input_value or not str(self.input_value).strip():
434
- msg = "Message cannot be empty. Please provide a valid message."
435
- raise ValueError(msg)
436
-
437
- try:
438
- llm_model, self.chat_history, self.tools = await self.get_agent_requirements()
439
-
440
- # Create agent message for event processing
441
- from lfx.schema.content_block import ContentBlock
442
- from lfx.schema.message import MESSAGE_SENDER_AI
443
-
444
- agent_message = Message(
445
- sender=MESSAGE_SENDER_AI,
446
- sender_name="Cuga",
447
- properties={"icon": "Bot", "state": "partial"},
448
- content_blocks=[ContentBlock(title="Agent Steps", contents=[])],
449
- session_id=self.graph.session_id,
450
- )
451
-
452
- # Get input text
453
- input_text = self.input_value.text if hasattr(self.input_value, "text") else str(self.input_value)
454
-
455
- # Create event iterator from call_agent
456
- event_iterator = self.call_agent(
457
- current_input=input_text, tools=self.tools or [], history_messages=self.chat_history, llm=llm_model
458
- )
459
-
460
- # Process events using the existing event processing system
461
- from lfx.base.agents.events import process_agent_events
462
-
463
- # Create a wrapper that forces DB updates for event handlers
464
- # This ensures the UI can see loading steps in real-time via polling
465
- async def force_db_update_send_message(message, id_=None, *, skip_db_update=False): # noqa: ARG001
466
- # Always persist to DB so polling-based UI shows loading steps in real-time
467
- content_blocks_len = len(message.content_blocks[0].contents) if message.content_blocks else 0
468
- logger.debug(
469
- f"[CUGA] Sending message update - state: {message.properties.state}, "
470
- f"content_blocks: {content_blocks_len}"
471
- )
472
- result = await self.send_message(message, id_=id_, skip_db_update=False)
473
- logger.debug(f"[CUGA] Message saved to DB with ID: {result.id if result else 'None'}")
474
- return result
475
-
476
- result = await process_agent_events(
477
- event_iterator, agent_message, cast("SendMessageFunctionType", force_db_update_send_message)
478
- )
479
-
480
- logger.info("[CUGA] Agent run finished successfully.")
481
- logger.info(f"[CUGA] Agent output: {result}")
482
-
483
- # Store result for potential JSON output
484
- self._agent_result = result
485
-
486
- except Exception as e:
487
- logger.error(f"[CUGA] Error in message_response: {e}")
488
- logger.error(f"An error occurred: {e!s}")
489
- logger.error(f"Traceback: {traceback.format_exc()}")
490
-
491
- # Check if error is related to Playwright installation
492
- error_str = str(e).lower()
493
- if "playwright install" in error_str:
494
- msg = (
495
- "Playwright is not installed. Please install Playwright Chromium using: "
496
- "uv run -m playwright install chromium"
497
- )
498
- raise ValueError(msg) from e
499
-
500
- raise
501
- else:
502
- return result
503
-
504
- async def get_agent_requirements(self):
505
- """Get the agent requirements for the Cuga agent.
506
-
507
- This method retrieves and configures all necessary components for the agent
508
- including the language model, chat history, and tools.
509
-
510
- Returns:
511
- tuple: A tuple containing (llm_model, chat_history, tools)
512
-
513
- Raises:
514
- ValueError: If no language model is selected or if there's an error
515
- in model initialization
516
- """
517
- llm_model, display_name = await self.get_llm()
518
- if llm_model is None:
519
- msg = "No language model selected. Please choose a model to proceed."
520
- raise ValueError(msg)
521
- self.model_name = get_model_name(llm_model, display_name=display_name)
522
-
523
- # Get memory data
524
- self.chat_history = await self.get_memory_data()
525
- if isinstance(self.chat_history, Message):
526
- self.chat_history = [self.chat_history]
527
-
528
- # Add current date tool if enabled
529
- if self.add_current_date_tool:
530
- if not isinstance(self.tools, list):
531
- self.tools = []
532
- current_date_tool = (await CurrentDateComponent(**self.get_base_args()).to_toolkit()).pop(0)
533
- if not isinstance(current_date_tool, StructuredTool):
534
- msg = "CurrentDateComponent must be converted to a StructuredTool"
535
- raise TypeError(msg)
536
- self.tools.append(current_date_tool)
537
-
538
- # --- ADDED LOGGING START ---
539
- logger.info("[CUGA] Retrieved agent requirements: LLM, chat history, and tools.")
540
- logger.info(f"[CUGA] LLM model: {self.model_name}")
541
- logger.info(f"[CUGA] Number of chat history messages: {len(self.chat_history)}")
542
- logger.info(f"[CUGA] Tools available: {[tool.name for tool in self.tools]}")
543
- logger.info(f"[CUGA] metadata: {[tool.metadata for tool in self.tools]}")
544
- # --- ADDED LOGGING END ---
545
-
546
- return llm_model, self.chat_history, self.tools
547
-
548
- def _preprocess_schema(self, schema):
549
- """Preprocess schema to ensure correct data types for build_model_from_schema.
550
-
551
- This method validates and normalizes the output schema to ensure it's compatible
552
- with the Pydantic model building process.
553
-
554
- Args:
555
- schema: List of schema field definitions
556
-
557
- Returns:
558
- list: Processed schema with validated data types
559
- """
560
- processed_schema = []
561
- for field in schema:
562
- processed_field = {
563
- "name": str(field.get("name", "field")),
564
- "type": str(field.get("type", "str")),
565
- "description": str(field.get("description", "")),
566
- "multiple": field.get("multiple", False),
567
- }
568
- # Ensure multiple is handled correctly
569
- if isinstance(processed_field["multiple"], str):
570
- processed_field["multiple"] = processed_field["multiple"].lower() in ["true", "1", "t", "y", "yes"]
571
- processed_schema.append(processed_field)
572
- return processed_schema
573
-
574
- async def build_structured_output_base(self, content: str):
575
- """Build structured output with optional BaseModel validation.
576
-
577
- This method parses JSON content from the agent response and optionally validates
578
- it against a provided schema using Pydantic models.
579
-
580
- Args:
581
- content: The raw content from the agent response
582
-
583
- Returns:
584
- dict or list: Parsed and optionally validated JSON data
585
- """
586
- # --- ADDED LOGGING START ---
587
- logger.info(f"[CUGA] Attempting to build structured output from content: {content}")
588
- # --- ADDED LOGGING END ---
589
-
590
- json_pattern = r"\{.*\}"
591
- schema_error_msg = "Try setting an output schema"
592
-
593
- # Try to parse content as JSON first
594
- json_data = None
595
- try:
596
- json_data = json.loads(content)
597
- except json.JSONDecodeError:
598
- json_match = re.search(json_pattern, content, re.DOTALL)
599
- if json_match:
600
- try:
601
- json_data = json.loads(json_match.group())
602
- except json.JSONDecodeError:
603
- logger.warning("[CUGA] Could not parse content as JSON even with regex match.")
604
- return {"content": content, "error": schema_error_msg}
605
- else:
606
- logger.warning("[CUGA] No JSON pattern found in the content.")
607
- return {"content": content, "error": schema_error_msg}
608
-
609
- # If no output schema provided, return parsed JSON without validation
610
- if not hasattr(self, "output_schema") or not self.output_schema or len(self.output_schema) == 0:
611
- logger.info("[CUGA] No output schema provided. Returning parsed JSON without validation.")
612
- return json_data
613
-
614
- # Use BaseModel validation with schema
615
- try:
616
- logger.info("[CUGA] Output schema detected. Validating structured output against schema.")
617
- processed_schema = self._preprocess_schema(self.output_schema)
618
- output_model = build_model_from_schema(processed_schema)
619
-
620
- # Validate against the schema
621
- if isinstance(json_data, list):
622
- # Multiple objects
623
- validated_objects = []
624
- for item in json_data:
625
- try:
626
- validated_obj = output_model.model_validate(item)
627
- validated_objects.append(validated_obj.model_dump())
628
- except ValidationError as e:
629
- await logger.aerror(f"[CUGA] Validation error for item: {e}")
630
- validated_objects.append({"data": item, "validation_error": str(e)})
631
- return validated_objects
632
-
633
- # Single object
634
- try:
635
- validated_obj = output_model.model_validate(json_data)
636
- return [validated_obj.model_dump()]
637
- except ValidationError as e:
638
- await logger.aerror(f"[CUGA] Validation error: {e}")
639
- return [{"data": json_data, "validation_error": str(e)}]
640
-
641
- except (TypeError, ValueError) as e:
642
- await logger.aerror(f"[CUGA] Error building structured output: {e}")
643
- return json_data
644
-
645
- async def json_response(self) -> Data:
646
- """Convert agent response to structured JSON Data output with schema validation.
647
-
648
- This method generates a structured JSON response by combining system instructions,
649
- format instructions, and schema information, then processing the agent's response
650
- through structured output validation.
651
-
652
- Returns:
653
- Data: Structured data object containing the validated JSON response
654
-
655
- Raises:
656
- ExceptionWithMessageError: If there's an error in structured processing
657
- ValueError: If there's a validation error
658
- TypeError: If there's a type error in processing
659
- """
660
- # --- ADDED LOGGING START ---
661
- logger.info("[CUGA] Starting Cuga agent run for json_response.")
662
- logger.info(f"[CUGA] Agent input value: {self.input_value}")
663
- # --- ADDED LOGGING END ---
664
-
665
- try:
666
- system_components = []
667
-
668
- # 1. Agent Instructions
669
- agent_instructions = getattr(self, "instructions", "") or ""
670
- if agent_instructions:
671
- system_components.append(f"{agent_instructions}")
672
-
673
- # 3. Format Instructions
674
- format_instructions = getattr(self, "format_instructions", "") or ""
675
- if format_instructions:
676
- system_components.append(f"Format instructions: {format_instructions}")
677
-
678
- # 4. Schema Information
679
- if hasattr(self, "output_schema") and self.output_schema and len(self.output_schema) > 0:
680
- try:
681
- processed_schema = self._preprocess_schema(self.output_schema)
682
- output_model = build_model_from_schema(processed_schema)
683
- schema_dict = output_model.model_json_schema()
684
- schema_info = (
685
- "You are given some text that may include format instructions, "
686
- "explanations, or other content alongside a JSON schema.\n\n"
687
- "Your task:\n"
688
- "- Extract only the JSON schema.\n"
689
- "- Return it as valid JSON.\n"
690
- "- Do not include format instructions, explanations, or extra text.\n\n"
691
- "Input:\n"
692
- f"{json.dumps(schema_dict, indent=2)}\n\n"
693
- "Output (only JSON schema):"
694
- )
695
- system_components.append(schema_info)
696
- except (ValidationError, ValueError, TypeError, KeyError) as e:
697
- await logger.aerror(f"[CUGA] Could not build schema for prompt: {e}", exc_info=True)
698
-
699
- # Combine all components
700
- combined_instructions = "\n\n".join(system_components) if system_components else ""
701
-
702
- llm_model, self.chat_history, self.tools = await self.get_agent_requirements()
703
-
704
- # Use call_agent for structured response
705
- input_text = self.input_value.text if hasattr(self.input_value, "text") else str(self.input_value)
706
-
707
- # Modify the input to include structured output requirements
708
- structured_input = (
709
- f"{combined_instructions}\n\nUser Input: {input_text}\n\nPlease provide a structured JSON response."
710
- )
711
-
712
- logger.info(f"[CUGA] Combined system prompt for structured agent: {combined_instructions}")
713
-
714
- content = await self.call_agent(
715
- current_input=structured_input,
716
- tools=self.tools or [],
717
- history_messages=self.chat_history,
718
- llm=llm_model,
719
- )
720
-
721
- logger.info(f"[CUGA] Structured agent result: {content}")
722
-
723
- except (ExceptionWithMessageError, ValueError, TypeError, NotImplementedError, AttributeError) as e:
724
- await logger.aerror(f"[CUGA] Error with structured agent: {e}")
725
- content_str = "No content returned from Cuga agent"
726
- return Data(data={"content": content_str, "error": str(e)})
727
-
728
- # Process with structured output validation
729
- try:
730
- structured_output = await self.build_structured_output_base(content)
731
-
732
- # Handle different output formats
733
- if isinstance(structured_output, list) and structured_output:
734
- if len(structured_output) == 1:
735
- logger.info("[CUGA] Structured output is a single object in a list.")
736
- logger.info(f"[CUGA] Final structured output: {structured_output[0]}")
737
- return Data(data=structured_output[0])
738
- logger.info("[CUGA] Structured output is a list of multiple objects.")
739
- logger.info(f"[CUGA] Final structured output: {structured_output}")
740
- return Data(data={"results": structured_output})
741
- if isinstance(structured_output, dict):
742
- logger.info("[CUGA] Structured output is a single dictionary.")
743
- logger.info(f"[CUGA] Final structured output: {structured_output}")
744
- return Data(data=structured_output)
745
- logger.info("[CUGA] Structured output is not a list or dictionary. Returning raw content.")
746
- logger.info(f"[CUGA] Final output content: {content}")
747
- return Data(data={"content": content})
748
-
749
- except (ValueError, TypeError) as e:
750
- await logger.aerror(f"[CUGA] Error in structured output processing: {e}")
751
- return Data(data={"content": content, "error": str(e)})
752
-
753
- async def get_memory_data(self):
754
- """Retrieve chat history messages.
755
-
756
- This method fetches the conversation history from memory, excluding the current
757
- input message to avoid duplication.
758
-
759
- Returns:
760
- list: List of Message objects representing the chat history
761
- """
762
- logger.info("[CUGA] Retrieving chat history messages.")
763
- messages = (
764
- await MemoryComponent(**self.get_base_args())
765
- .set(session_id=self.graph.session_id, order="Ascending", n_messages=self.n_messages)
766
- .retrieve_messages()
767
- )
768
- return [
769
- message for message in messages if getattr(message, "id", None) != getattr(self.input_value, "id", None)
770
- ]
771
-
772
- async def get_llm(self):
773
- """Get language model for the Cuga agent.
774
-
775
- This method initializes and configures the language model based on the
776
- selected provider and parameters.
777
-
778
- Returns:
779
- tuple: A tuple containing (llm_model, display_name)
780
-
781
- Raises:
782
- ValueError: If the model provider is invalid or model initialization fails
783
- """
784
- logger.info("[CUGA] Getting language model for the agent.")
785
- logger.info(f"[CUGA] Requested LLM provider: {self.agent_llm}")
786
-
787
- if not isinstance(self.agent_llm, str):
788
- logger.info("[CUGA] Agent LLM is already a model instance.")
789
- return self.agent_llm, None
790
-
791
- try:
792
- provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)
793
- if not provider_info:
794
- msg = f"Invalid model provider: {self.agent_llm}"
795
- raise ValueError(msg)
796
-
797
- component_class = provider_info.get("component_class")
798
- display_name = component_class.display_name
799
- inputs = provider_info.get("inputs")
800
- prefix = provider_info.get("prefix", "")
801
- logger.info(f"[CUGA] Successfully built LLM model from provider: {self.agent_llm}")
802
- return self._build_llm_model(component_class, inputs, prefix), display_name
803
-
804
- except (AttributeError, ValueError, TypeError, RuntimeError) as e:
805
- await logger.aerror(f"[CUGA] Error building {self.agent_llm} language model: {e!s}")
806
- msg = f"Failed to initialize language model: {e!s}"
807
- raise ValueError(msg) from e
808
-
809
- def _build_llm_model(self, component, inputs, prefix=""):
810
- """Build LLM model with parameters.
811
-
812
- This method constructs a language model instance using the provided component
813
- class and input parameters.
814
-
815
- Args:
816
- component: The LLM component class to instantiate
817
- inputs: List of input field definitions
818
- prefix: Optional prefix for parameter names
819
-
820
- Returns:
821
- The configured LLM model instance
822
- """
823
- model_kwargs = {}
824
- for input_ in inputs:
825
- if hasattr(self, f"{prefix}{input_.name}"):
826
- model_kwargs[input_.name] = getattr(self, f"{prefix}{input_.name}")
827
- return component.set(**model_kwargs).build_model()
828
-
829
- def set_component_params(self, component):
830
- """Set component parameters based on provider.
831
-
832
- This method configures component parameters according to the selected
833
- model provider's requirements.
834
-
835
- Args:
836
- component: The component to configure
837
-
838
- Returns:
839
- The configured component
840
- """
841
- provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)
842
- if provider_info:
843
- inputs = provider_info.get("inputs")
844
- prefix = provider_info.get("prefix")
845
- model_kwargs = {}
846
- for input_ in inputs:
847
- if hasattr(self, f"{prefix}{input_.name}"):
848
- model_kwargs[input_.name] = getattr(self, f"{prefix}{input_.name}")
849
- return component.set(**model_kwargs)
850
- return component
851
-
852
- def delete_fields(self, build_config: dotdict, fields: dict | list[str]) -> None:
853
- """Delete specified fields from build_config.
854
-
855
- This method removes unwanted fields from the build configuration.
856
-
857
- Args:
858
- build_config: The build configuration dictionary
859
- fields: Fields to remove (can be dict or list of strings)
860
- """
861
- for field in fields:
862
- build_config.pop(field, None)
863
-
864
- def update_input_types(self, build_config: dotdict) -> dotdict:
865
- """Update input types for all fields in build_config.
866
-
867
- This method ensures all fields in the build configuration have proper
868
- input types defined.
869
-
870
- Args:
871
- build_config: The build configuration to update
872
-
873
- Returns:
874
- dotdict: Updated build configuration with input types
875
- """
876
- for key, value in build_config.items():
877
- if isinstance(value, dict):
878
- if value.get("input_types") is None:
879
- build_config[key]["input_types"] = []
880
- elif hasattr(value, "input_types") and value.input_types is None:
881
- value.input_types = []
882
- return build_config
883
-
884
- async def update_build_config(
885
- self, build_config: dotdict, field_value: str, field_name: str | None = None
886
- ) -> dotdict:
887
- """Update build configuration based on field changes.
888
-
889
- This method dynamically updates the component's build configuration when
890
- certain fields change, particularly the model provider selection.
891
-
892
- Args:
893
- build_config: The current build configuration
894
- field_value: The new value for the field
895
- field_name: The name of the field being changed
896
-
897
- Returns:
898
- dotdict: Updated build configuration
899
-
900
- Raises:
901
- ValueError: If required keys are missing from the configuration
902
- """
903
- if field_name in ("agent_llm",):
904
- build_config["agent_llm"]["value"] = field_value
905
- provider_info = MODEL_PROVIDERS_DICT.get(field_value)
906
- if provider_info:
907
- component_class = provider_info.get("component_class")
908
- if component_class and hasattr(component_class, "update_build_config"):
909
- build_config = await update_component_build_config(
910
- component_class, build_config, field_value, "model_name"
911
- )
912
-
913
- provider_configs: dict[str, tuple[dict, list[dict]]] = {
914
- provider: (
915
- MODEL_PROVIDERS_DICT[provider]["fields"],
916
- [
917
- MODEL_PROVIDERS_DICT[other_provider]["fields"]
918
- for other_provider in MODEL_PROVIDERS_DICT
919
- if other_provider != provider
920
- ],
921
- )
922
- for provider in MODEL_PROVIDERS_DICT
923
- }
924
- if field_value in provider_configs:
925
- fields_to_add, fields_to_delete = provider_configs[field_value]
926
-
927
- # Delete fields from other providers
928
- for fields in fields_to_delete:
929
- self.delete_fields(build_config, fields)
930
-
931
- # Add provider-specific fields
932
- if field_value == "OpenAI" and not any(field in build_config for field in fields_to_add):
933
- build_config.update(fields_to_add)
934
- else:
935
- build_config.update(fields_to_add)
936
- build_config["agent_llm"]["input_types"] = []
937
- elif field_value == "Custom":
938
- # Delete all provider fields
939
- self.delete_fields(build_config, ALL_PROVIDER_FIELDS)
940
- # Update with custom component
941
- custom_component = DropdownInput(
942
- name="agent_llm",
943
- display_name="Language Model",
944
- options=[*sorted(MODEL_PROVIDERS), "Custom"],
945
- value="Custom",
946
- real_time_refresh=True,
947
- input_types=["LanguageModel"],
948
- options_metadata=[MODELS_METADATA[key] for key in sorted(MODELS_METADATA.keys())]
949
- + [{"icon": "brain"}],
950
- )
951
- build_config.update({"agent_llm": custom_component.to_dict()})
952
-
953
- # Update input types for all fields
954
- build_config = self.update_input_types(build_config)
955
-
956
- # Validate required keys
957
- default_keys = [
958
- "code",
959
- "_type",
960
- "agent_llm",
961
- "tools",
962
- "input_value",
963
- "add_current_date_tool",
964
- "policies",
965
- "agent_description",
966
- "max_iterations",
967
- "handle_parsing_errors",
968
- "verbose",
969
- ]
970
- missing_keys = [key for key in default_keys if key not in build_config]
971
- if missing_keys:
972
- msg = f"Missing required keys in build_config: {missing_keys}"
973
- raise ValueError(msg)
974
-
975
- if (
976
- isinstance(self.agent_llm, str)
977
- and self.agent_llm in MODEL_PROVIDERS_DICT
978
- and field_name in MODEL_DYNAMIC_UPDATE_FIELDS
979
- ):
980
- provider_info = MODEL_PROVIDERS_DICT.get(self.agent_llm)
981
- if provider_info:
982
- component_class = provider_info.get("component_class")
983
- component_class = self.set_component_params(component_class)
984
- prefix = provider_info.get("prefix")
985
- if component_class and hasattr(component_class, "update_build_config"):
986
- if isinstance(field_name, str) and isinstance(prefix, str):
987
- field_name = field_name.replace(prefix, "")
988
- build_config = await update_component_build_config(
989
- component_class, build_config, field_value, "model_name"
990
- )
991
- return dotdict({k: v.to_dict() if hasattr(v, "to_dict") else v for k, v in build_config.items()})
992
-
993
- async def _get_tools(self) -> list[Tool]:
994
- """Build agent tools.
995
-
996
- This method constructs the list of tools available to the Cuga agent,
997
- including component tools and any additional configured tools.
998
-
999
- Returns:
1000
- list[Tool]: List of available tools for the agent
1001
- """
1002
- logger.info("[CUGA] Building agent tools.")
1003
- component_toolkit = _get_component_toolkit()
1004
- tools_names = self._build_tools_names()
1005
- agent_description = self.get_tool_description()
1006
- description = f"{agent_description}{tools_names}"
1007
- tools = component_toolkit(component=self).get_tools(
1008
- tool_name="Call_CugaAgent", tool_description=description, callbacks=self.get_langchain_callbacks()
1009
- )
1010
- if hasattr(self, "tools_metadata"):
1011
- tools = component_toolkit(component=self, metadata=self.tools_metadata).update_tools_metadata(tools=tools)
1012
- logger.info(f"[CUGA] Tools built: {[tool.name for tool in tools]}")
1013
- return tools