astonish 0.1__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.

Potentially problematic release.


This version of astonish might be problematic. Click here for more details.

astonish/__init__.py ADDED
File without changes
@@ -0,0 +1,406 @@
1
+ description: An agentic flow designed to assist users in creating a new agentic flows.
2
+ nodes:
3
+ - name: get_overall_idea
4
+ type: input
5
+ prompt: |
6
+ Welcome to the Agentic Flow Creator! I'm here to help you design a new agentic flow.
7
+ We are going to create the initial flow for your agentic flow, which you customize later.
8
+ Please provide an overall description of the flow you want to create. What's the main goal and what should it do?
9
+
10
+ output_model:
11
+ overall_idea: str
12
+ user_message:
13
+ - "Thank you for sharing your idea! Let's now check which tools you have currently available."
14
+
15
+ - name: fetch_available_tools
16
+ type: llm
17
+ tools: True
18
+ tools_selection:
19
+ - shell_command
20
+ system: |
21
+ You are a system assistant tasked with fetching the list of available tools.
22
+ prompt: |
23
+ Use the shell_command tool to run the following command:
24
+ astonish tools list
25
+
26
+ Parse the output and create a structured list of available tools with their descriptions.
27
+ output_model:
28
+ available_tools: list
29
+ user_message:
30
+ - "Available tools fetched successfully."
31
+
32
+ - name: process_idea
33
+ type: llm
34
+ system: |
35
+ You are an expert in designing agentic flows. Analyze the user's overall idea and create a base flow structure.
36
+ prompt: |
37
+ User's overall idea:
38
+ {overall_idea}
39
+
40
+ Available tools:
41
+ {available_tools}
42
+
43
+ Based on this idea and the available tools, please:
44
+ 1. Create a base flow structure with necessary nodes and their purposes.
45
+ 2. Identify which of the available tools might be required for this flow.
46
+ 3. Highlight any potential challenges or areas that need further clarification.
47
+ 4. If any required functionality is not available in the current tools, note it as a potential limitation.
48
+ 5. Remember that input from the user can be collected directly from the application, so you don't need to require a tool for that.
49
+
50
+ Present this information in a clear, structured format.
51
+ Export the base_flow if line breaks between the items using \n
52
+
53
+ If the tools are not sufficient to implement the flow, please indicate that the user should provide the specific tools throug MCP servers to be able to create the agents.
54
+
55
+ add the the message for the user, detailing the base flow, required tools, challenges, and limitations, in the response message
56
+ Structure the message to the user as follows:
57
+
58
+ I've created a base flow structure based on your idea. Here it is, along with required tools, potential challenges, and limitations:
59
+
60
+ Base Flow:
61
+ {base_flow}
62
+
63
+ Required Tools:
64
+ {required_tools}
65
+
66
+ Potential Challenges:
67
+ {challenges}
68
+
69
+ Limitations:
70
+ {limitations}
71
+
72
+ For any item that has a list (eg.: ['item1','item2']), please break in multiple lines and put * before each item.
73
+ Do not use tools for analysis, format, or any other tasks that can be done by the LLM itself.
74
+ output_model:
75
+ base_flow: list
76
+ required_tools: list
77
+ challenges: str
78
+ limitations: str
79
+ have_tools: bool
80
+ response: str
81
+ user_message:
82
+ - response
83
+ print_state: False
84
+
85
+ - name: get_user_feedback
86
+ type: input
87
+ prompt: |
88
+ Do you have any feedback or changes you'd like to make? If you're satisfied, type 'CONTINUE'. Otherwise, provide your feedback:
89
+ output_model:
90
+ user_feedback: str
91
+
92
+ - name: refine_flow
93
+ type: llm
94
+ system: |
95
+ You are an agentic flow architect. Refine the flow based on user feedback.
96
+ prompt: |
97
+ Current base flow:
98
+ {base_flow}
99
+
100
+ User feedback:
101
+ {user_feedback}
102
+
103
+ Available tools:
104
+ {available_tools}
105
+
106
+ Please refine the flow, addressing the user's feedback. If the user typed asked to continue, keep the current flow as is.
107
+ Ensure that any changes still work within the constraints of the available tools.
108
+ output_model:
109
+ refined_flow: str
110
+ continue_flag: bool
111
+
112
+ - name: generate_yaml
113
+ type: llm
114
+ system: |
115
+ You are an expert in creating agentic flow YAML files. Follow the provided example and rules strictly.
116
+ prompt: |
117
+ Refined flow:
118
+ {refined_flow}
119
+
120
+ Available tools:
121
+ {available_tools}
122
+
123
+ Generate a complete YAML file for this agentic flow, following this specific format and rules:
124
+
125
+ Rules:
126
+ 1. Node types can only be 'input' or 'llm'.
127
+ 2. 'input' nodes have limited fields: name, type, prompt, and output_model.
128
+ 3. 'llm' nodes can have all fields: name, type, system, prompt, output_model, user_message, tools, and tools_selection.
129
+ 4. 'tools' field should be a boolean (True/False).
130
+ 5. 'tools_selection' should only be included if 'tools' is True.
131
+ 6. 'tools_selection' should only contain tools from the available_tools list and should only contain the tool name, not the description.
132
+ 7. 'user_message' should only be included if we need to display something to the user, and should only contain fields defined in the output_model.
133
+ 8. Ensure all node names are unique and descriptive.
134
+ 9. The flow must cover all possible paths through the nodes, including conditional branches.
135
+ 10. Conditions must be properly formatted as lambda functions.
136
+ 11. Only use tools that are available in the {available_tools} list.
137
+ 12. Ensure all variables used in prompts are defined in the output_model of a previous node.
138
+ 13. Be careful with the content of the user_message, as it should only contain the fields defined in the output_model of the node.
139
+ 14. When adding a variable to the user_message, we should ensure that we request in the prompt for the content to be displayed to be well-formatted.
140
+ 15. Pay attention to not provided duplicate content to the user, as it can cause confusion.
141
+ 16. Make sure all variables used in prompts are properly defined in the output_model of previous nodes.
142
+ 17. Make sure variables in a node prompot are defined in the output_model of previous nodes.
143
+ 18. Make sure the node use the required variables in the prompt
144
+
145
+ Example YAML structure (note: variables in this example are prefixed with 'example_' to distinguish them from actual state variables):
146
+
147
+ ```yaml
148
+ nodes:
149
+ - name: get_user_input
150
+ type: input
151
+ prompt: |
152
+ Please enter your question or type 'exit' to quit:
153
+ output_model:
154
+ example_user_input: str
155
+
156
+ - name: analyze_input
157
+ type: llm
158
+ system: |
159
+ You are an AI assistant analyzing user input.
160
+ prompt: |
161
+ Analyze the following user input: {{example_user_input}}
162
+ Determine if it's a question or an exit command.
163
+ output_model:
164
+ example_is_question: bool
165
+ example_input_type: str
166
+
167
+ - name: process_question
168
+ type: llm
169
+ system: |
170
+ You are an AI assistant answering user questions.
171
+ prompt: |
172
+ User question: {{example_user_input}}
173
+ Please provide a detailed answer to the user's question.
174
+ output_model:
175
+ example_answer: str
176
+ user_message:
177
+ - example_answer
178
+ tools: True
179
+ tools_selection:
180
+ - web_search
181
+ - knowledge_base
182
+
183
+ - name: farewell
184
+ type: llm
185
+ system: |
186
+ You are a polite AI assistant saying goodbye.
187
+ prompt: |
188
+ Generate a polite farewell message for the user.
189
+ output_model:
190
+ example_farewell_message: str
191
+ user_message:
192
+ - example_farewell_message
193
+
194
+ flow:
195
+ - from: START
196
+ to: get_user_input
197
+ - from: get_user_input
198
+ to: analyze_input
199
+ - from: analyze_input
200
+ edges:
201
+ - to: process_question
202
+ condition: "lambda x: x['example_is_question']"
203
+ - to: farewell
204
+ condition: "lambda x: not x['example_is_question']"
205
+ - from: process_question
206
+ to: get_user_input
207
+ - from: farewell
208
+ to: END
209
+ ```
210
+
211
+ Now, generate the complete YAML for the agentic flow based on the refined flow structure we've discussed, following the example and rules provided. Make sure to include appropriate conditional flows, only use user_message when necessary, and ensure all variables used in prompts are properly defined in previous nodes' output_models.
212
+
213
+ After generating the YAML, show the content to the user.
214
+
215
+ output_model:
216
+ flow_yaml: str
217
+ save_confirmation: str
218
+ user_message:
219
+ - flow_yaml
220
+ print_state: False
221
+
222
+ - name: get_user_feedback_yaml
223
+ type: input
224
+ prompt: |
225
+ Do you have any feedback or changes you'd like to make? If you're satisfied, type 'CONTINUE'. Otherwise, provide your feedback:
226
+ output_model:
227
+ user_feedback_yaml: str
228
+
229
+ - name: refine_yaml
230
+ type: llm
231
+ system: |
232
+ You are an agentic flow architect. Refine the yaml flow based on user feedback.
233
+ prompt: |
234
+ Current yaml flow:
235
+ {refined_flow}
236
+
237
+ User feedback:
238
+ {user_feedback_yaml}
239
+
240
+ Available tools:
241
+ {available_tools}
242
+
243
+ Please refine the flow, addressing the user's feedback. If the user asked to continue, keep the current yaml flow as is.
244
+ Ensure that any changes still work within the constraints of the available tools.
245
+ Only change what is necessary to address the user's feedback, nothing else
246
+ output_model:
247
+ refined_flow: str
248
+ continue_yaml_flag: bool
249
+
250
+ - name: save_yaml
251
+ type: llm
252
+ tools: True
253
+ tools_selection:
254
+ - write_file
255
+ - shell_command
256
+ system: |
257
+ You are an expert in creating and saving files to disk. Follow the provided example and rules strictly.
258
+ prompt: |
259
+ Run following command with the shell_command tool to get the path to the user config directory:
260
+ python -c "import appdirs; print(appdirs.user_config_dir('astonish'))"
261
+
262
+ flow_yaml:
263
+ {flow_yaml}
264
+
265
+ Save the content of the flow_yaml to disk using write_file tool in the config directory.
266
+ The file name should be at 'agents/new_flow.yaml' inside the config_dir.
267
+
268
+ Confirm once the file has been saved.
269
+ output_model:
270
+ config_dir: str
271
+ flow_yaml: str
272
+ save_confirmation: str
273
+ user_message:
274
+ - save_confirmation
275
+ print_state: False
276
+
277
+ - name: validate_flow
278
+ type: llm
279
+ tools: True
280
+ tools_selection:
281
+ - shell_command
282
+ system: |
283
+ You are a system tester for agentic flows. Run the validation command and interpret the results.
284
+ prompt: |
285
+ Refined flow:
286
+ {refined_flow}
287
+
288
+ The flow YAML has been saved to:
289
+ /home/schardosin/projects/astonish/astonish/agents/new_flow.yaml
290
+
291
+ Use the shell_command tool to run the following command:
292
+ astonish flow new_flow
293
+
294
+ Analyze the output and determine if there are any errors or warnings. If there are issues, summarize them clearly.
295
+ Format the content before displaying it to the user.
296
+
297
+ Verify that all the requirements in the Refined flow were met and that the flow is valid.
298
+ Store the shell command output in the shell_result variable.
299
+ output_model:
300
+ validation_result: str
301
+ has_errors: bool
302
+ shell_result: str
303
+ user_message:
304
+ - shell_result
305
+ - validation_result
306
+
307
+ - name: fix_errors
308
+ type: llm
309
+ tools: True
310
+ tools_selection:
311
+ - read_file
312
+ - write_file
313
+ system: |
314
+ You are an agentic flow debugger. Fix any errors found during validation.
315
+ prompt: |
316
+ Validation results:
317
+ {validation_result}
318
+
319
+ Available tools:
320
+ {available_tools}
321
+
322
+ Use the FileReadOrEdit tool to read the current flow YAML from:
323
+ /home/schardosin/projects/astonish/astonish/agents/new_flow.yaml
324
+
325
+ Please fix any errors found during validation. If no errors were found, return the current YAML unchanged.
326
+ Ensure that any fixes still use only the available tools.
327
+
328
+ After making any necessary changes, use the FileWriteOrEdit tool to save the updated YAML back to the same file.
329
+ output_model:
330
+ fixed_yaml: str
331
+ save_confirmation: str
332
+ user_message:
333
+ - save_confirmation
334
+
335
+ - name: show_final_flow
336
+ type: llm
337
+ system: |
338
+ You are a helpful assistant presenting the final agentic flow to the user.
339
+ prompt: |
340
+ Use the FileReadOrEdit tool to read the final flow YAML from:
341
+ /home/schardosin/projects/astonish/astonish/agents/new_flow.yaml
342
+
343
+ Please present a summary of the final flow to the user, highlighting its key components and functionality.
344
+ Also mention which of the available tools are used in this flow.
345
+ output_model:
346
+ flow_summary: str
347
+ user_message:
348
+ - flow_summary
349
+
350
+ - name: get_final_approval
351
+ type: input
352
+ prompt: |
353
+ This is the final version of your agentic flow. Do you approve saving this flow? (yes/no)
354
+ output_model:
355
+ final_approval: bool
356
+
357
+ flow:
358
+ - from: START
359
+ to: get_overall_idea
360
+ - from: get_overall_idea
361
+ to: fetch_available_tools
362
+ - from: fetch_available_tools
363
+ to: process_idea
364
+ - from: process_idea
365
+ edges:
366
+ - to: get_user_feedback
367
+ condition: "lambda x: x['have_tools']"
368
+ - to: END
369
+ condition: "lambda x: not x['have_tools']"
370
+ - from: get_user_feedback
371
+ to: refine_flow
372
+ - from: refine_flow
373
+ edges:
374
+ - to: get_user_feedback_yaml
375
+ condition: "lambda x: not x['continue_flag']"
376
+ - to: generate_yaml
377
+ condition: "lambda x: x['continue_flag']"
378
+ - from: generate_yaml
379
+ to: get_user_feedback_yaml
380
+ - from: get_user_feedback_yaml
381
+ to: refine_yaml
382
+ - from: refine_yaml
383
+ edges:
384
+ - to: generate_yaml
385
+ condition: "lambda x: not x['continue_yaml_flag']"
386
+ - to: save_yaml
387
+ condition: "lambda x: x['continue_yaml_flag']"
388
+ - from: validate_flow
389
+ edges:
390
+ - to: fix_errors
391
+ condition: "lambda x: x['has_errors']"
392
+ - to: show_final_flow
393
+ condition: "lambda x: not x['has_errors']"
394
+
395
+ - from: fix_errors
396
+ to: validate_flow
397
+ - from: show_final_flow
398
+ to: get_final_approval
399
+ - from: get_final_approval
400
+ edges:
401
+ - to: save_yaml
402
+ condition: "lambda x: x['final_approval']"
403
+ - to: get_user_feedback_yaml
404
+ condition: "lambda x: not x['final_approval']"
405
+ - from: save_yaml
406
+ to: END
File without changes
@@ -0,0 +1,27 @@
1
+ from astonish.providers.ai_provider_interface import AIProvider
2
+ from astonish.providers.sap_ai_core_provider import SAPAICoreProvider
3
+ from astonish.providers.ollama_provider import OllamaProvider
4
+ from typing import List, Tuple
5
+
6
+ class AIProviderFactory:
7
+ _providers = {}
8
+
9
+ @classmethod
10
+ def register_provider(cls, name: str, display_name: str, provider_class: type):
11
+ if not issubclass(provider_class, AIProvider):
12
+ raise TypeError("Provider must implement AIProvider interface")
13
+ cls._providers[name] = (display_name, provider_class)
14
+
15
+ @classmethod
16
+ def get_provider(cls, name: str) -> AIProvider:
17
+ provider_info = cls._providers.get(name)
18
+ if not provider_info:
19
+ raise ValueError(f"No provider registered with name: {name}")
20
+ return provider_info[1]()
21
+
22
+ @classmethod
23
+ def get_registered_providers(cls) -> List[Tuple[str, str]]:
24
+ return [(name, info[0]) for name, info in cls._providers.items()]
25
+
26
+ AIProviderFactory.register_provider("sap_ai_core", "SAP AI Core", SAPAICoreProvider)
27
+ AIProviderFactory.register_provider("ollama", "Ollama", OllamaProvider)
astonish/globals.py ADDED
@@ -0,0 +1,102 @@
1
+ import configparser, os, appdirs
2
+ import logging
3
+ from logging.handlers import RotatingFileHandler
4
+
5
+ CONFIG_FILE = "config.ini"
6
+ LOG_FILE = "astonish.log"
7
+ MCP_CONFIG_FILE = "mcp_config.json"
8
+
9
+ config = configparser.ConfigParser()
10
+ config_dir = appdirs.user_config_dir("astonish")
11
+ os.makedirs(config_dir, exist_ok=True)
12
+ config_path = os.path.join(config_dir, CONFIG_FILE)
13
+ logger_path = os.path.join(config_dir, LOG_FILE)
14
+ mcp_config_path = os.path.join(config_dir, MCP_CONFIG_FILE)
15
+ mcp_config = None
16
+
17
+ # Configure the logger
18
+ logging.basicConfig(
19
+ level=logging.INFO, # Default log level
20
+ format="%(asctime)s - %(levelname)s - %(message)s",
21
+ handlers=[
22
+ RotatingFileHandler(logger_path, maxBytes=5 * 1024 * 1024, backupCount=3), # 5 MB files
23
+ ],
24
+ )
25
+
26
+ logger = logging.getLogger("astonish")
27
+
28
+ def setup_logger(verbose=False):
29
+ logger.setLevel(logging.DEBUG if verbose else logging.INFO)
30
+
31
+ # File handler
32
+ file_handler = RotatingFileHandler(logger_path, maxBytes=5 * 1024 * 1024, backupCount=3)
33
+ file_handler.setFormatter(logging.Formatter("%(asctime)s - %(levelname)s - %(message)s"))
34
+ logger.addHandler(file_handler)
35
+
36
+ # Optional terminal handler for verbose mode
37
+ if verbose:
38
+ console_handler = logging.StreamHandler()
39
+ console_handler.setFormatter(logging.Formatter("%(levelname)s: %(message)s"))
40
+ logger.addHandler(console_handler)
41
+
42
+ return logger
43
+
44
+ def load_config():
45
+ # Load existing configuration if it exists
46
+ if os.path.exists(config_path):
47
+ config.read(config_path)
48
+
49
+ def load_mcp_config():
50
+ import json
51
+
52
+ global mcp_config
53
+ default_config = {
54
+ "mcpServers": {}
55
+ }
56
+
57
+ # Load existing MCP configuration if it exists
58
+ if os.path.exists(mcp_config_path):
59
+ try:
60
+ with open(mcp_config_path, 'r') as mcp_config_file:
61
+ loaded_config = json.load(mcp_config_file)
62
+
63
+ # Check if 'mcpServers' key exists in the loaded config
64
+ if "mcpServers" not in loaded_config:
65
+ logger.warning(f"'mcpServers' key not found in {mcp_config_path}. Using default configuration.")
66
+ mcp_config = default_config
67
+ else:
68
+ mcp_config = loaded_config
69
+ except json.JSONDecodeError:
70
+ logger.warning(f"Error decoding JSON from {mcp_config_path}. Using default configuration.")
71
+ mcp_config = default_config
72
+ except Exception as e:
73
+ logger.warning(f"Error reading {mcp_config_path}: {str(e)}. Using default configuration.")
74
+ mcp_config = default_config
75
+ else:
76
+ logger.warning(f"MCP config file not found at {mcp_config_path}. Using default configuration.")
77
+ mcp_config = default_config
78
+
79
+ return mcp_config
80
+
81
+ async def initialize_mcp_tools():
82
+ from langchain_mcp_adapters.client import MultiServerMCPClient
83
+
84
+ global mcp_config
85
+ if mcp_config is None:
86
+ logger.error("MCP config is not loaded. Cannot initialize MCP tools.")
87
+ return None
88
+
89
+ logger.info("Initializing MCP client...")
90
+ mcp_client = MultiServerMCPClient(mcp_config['mcpServers'])
91
+
92
+ logger.info("Connecting to MCP servers...")
93
+ for server_name, server_config in mcp_config['mcpServers'].items():
94
+ try:
95
+ logger.info(f"Connecting to server: {server_name}")
96
+ await mcp_client.connect_to_server(server_name, server_config)
97
+ logger.info(f"Successfully connected to server: {server_name}")
98
+ except Exception as e:
99
+ logger.error(f"Failed to connect to server {server_name}: {str(e)}")
100
+
101
+ logger.info("MCP client initialization complete")
102
+ return mcp_client