jonathanjordan21 commited on
Commit
18f5c57
·
verified ·
1 Parent(s): 6cd8929

Upload folder using huggingface_hub

Browse files
Dockerfile ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Read the doc: https://huggingface.co/docs/hub/spaces-sdks-docker
2
+ # you will also find guides on how best to write your Dockerfile
3
+
4
+ FROM python:latest
5
+
6
+ RUN useradd -m -u 1000 user
7
+ USER user
8
+ ENV PATH="/home/user/.local/bin:$PATH"
9
+
10
+ WORKDIR /app
11
+
12
+ COPY --chown=user ./requirements.txt requirements.txt
13
+ RUN pip install --no-cache-dir --upgrade -r requirements.txt
14
+
15
+ COPY --chown=user . /app
16
+ CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7860"]
README.md ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ ---
2
+ title: Blank Docker
3
+ emoji: 🦀
4
+ colorFrom: red
5
+ colorTo: pink
6
+ sdk: docker
7
+ pinned: false
8
+ ---
9
+
10
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
__pycache__/app.cpython-311.pyc ADDED
Binary file (9.85 kB). View file
 
__pycache__/chains.cpython-311.pyc ADDED
Binary file (23 kB). View file
 
__pycache__/models.cpython-311.pyc ADDED
Binary file (2.83 kB). View file
 
app.py ADDED
@@ -0,0 +1,224 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from fastapi import FastAPI
2
+ from langchain_core.messages.base import message_to_dict
3
+ from .models import Agent, OutputCollector, APIEndpoint
4
+ from .chains import GeneralStates, build_chain, agent_builder
5
+ # from chains import *
6
+ from langgraph.checkpoint.memory import MemorySaver
7
+ from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
8
+ from langgraph.graph import StateGraph, MessagesState, START, END
9
+ from datetime import datetime
10
+
11
+ from typing import List
12
+
13
+ app = FastAPI()
14
+ checkpointer = MemorySaver()
15
+
16
+
17
+ projects_chains = {
18
+
19
+ }
20
+
21
+ projects_agents = {
22
+
23
+ }
24
+
25
+ projects_output_collectors = {
26
+
27
+ }
28
+
29
+ projects_input_api_endpoints = {
30
+
31
+ }
32
+
33
+ projects_output_api_endpoints = {
34
+
35
+ }
36
+
37
+
38
+ @app.post("/agents")
39
+ def create_agent(data: Agent):
40
+ current_time = datetime.utcnow()
41
+ data_dict = data.dict()
42
+ data_dict["created_at"] = current_time
43
+ data_dict["updated_at"] = current_time
44
+ data_dict["deleted_at"] = None
45
+
46
+ if data_dict["output_collector"]:
47
+ project_collectors = projects_output_collectors.get(data_dict['project_id'])
48
+ collector = project_collectors.get(data_dict['output_collector'])
49
+ data_dict["output_collector"] = [f"{collector['keys'][i]}: {collector['data_types'][i]} = {collector['descriptions'][i]}" for i in range(len(collector['keys']))]
50
+ if data_dict["input_api_endpoints"]:
51
+ project_input_api = projects_input_api_endpoints.get(data_dict['project_id'])
52
+ input_api = [project_input_api.get(inp) for inp in data_dict['input_api_endpoints']]
53
+ data_dict['input_api_endpoints'] = input_api
54
+ if data_dict["output_api_endpoints"]:
55
+ project_output_api = projects_output_api_endpoints.get(data_dict['project_id'])
56
+ output_api = [project_output_api.get(out) for out in data_dict['output_api_endpoints']]
57
+ data_dict['output_api_endpoints'] = output_api
58
+
59
+ if not projects_agents.get(data_dict['project_id']):
60
+ projects_agents[data_dict['project_id']] = {data_dict['id']: data_dict}
61
+ else:
62
+ projects_agents[data_dict['project_id']].update({data_dict['id']: data_dict})
63
+
64
+
65
+ return {"message": "Agent created", "data": data_dict}
66
+
67
+
68
+ @app.post("/output_collectors")
69
+ def create_output_collector(data: OutputCollector):
70
+ current_time = datetime.utcnow()
71
+ data_dict = data.dict()
72
+ data_dict["created_at"] = current_time
73
+ data_dict["updated_at"] = current_time
74
+ data_dict["deleted_at"] = None
75
+ if not projects_output_collectors.get(data_dict['project_id']):
76
+ projects_output_collectors[data_dict['project_id']] = {data_dict['id']: data_dict}
77
+ else:
78
+ projects_output_collectors[data_dict['project_id']].update({data_dict['id']: data_dict})
79
+ return {"message": "Output Collector created", "data": data_dict}
80
+
81
+
82
+ @app.post("/output_api_endpoints")
83
+ def create_input_api(data: APIEndpoint):
84
+ current_time = datetime.utcnow()
85
+ data_dict = data.dict()
86
+ data_dict["created_at"] = current_time
87
+ data_dict["updated_at"] = current_time
88
+ data_dict["deleted_at"] = None
89
+ if not projects_input_api_endpoints.get(data_dict['project_id']):
90
+ projects_input_api_endpoints[data_dict['project_id']] = {data_dict['id']: data_dict}
91
+ else:
92
+ projects_input_api_endpoints[data_dict['project_id']].update({data_dict['id']: data_dict})
93
+ return {"message": "Input API Endpoint created", "data": data_dict}
94
+
95
+
96
+ @app.post("/output_api_endpoints")
97
+ def create_output_api(data: APIEndpoint):
98
+ current_time = datetime.utcnow()
99
+ data_dict = data.dict()
100
+ data_dict["created_at"] = current_time
101
+ data_dict["updated_at"] = current_time
102
+ data_dict["deleted_at"] = None
103
+ if not projects_output_api_endpoints.get(data_dict['project_id']):
104
+ projects_output_api_endpoints[data_dict['project_id']] = {data_dict['id']: data_dict}
105
+ else:
106
+ projects_output_api_endpoints[data_dict['project_id']].update({data_dict['id']: data_dict})
107
+ return {"message": "Output API Endpoint created", "data": data_dict}
108
+
109
+
110
+ @app.post("/build_chain")
111
+ def create_chain(project_id: str, id:str, chains: List[dict], welcome_message: str):
112
+ current_time = datetime.utcnow()
113
+ # data_dict = {}
114
+
115
+ # builder = StateGraph(GeneralStates)
116
+ agents = projects_agents.get(project_id)
117
+
118
+ def update_key(data, key_field, agents):
119
+ if isinstance(data, dict):
120
+ # If the current item is a dictionary, update the "key" field if it exists
121
+ if key_field in data:
122
+ data[key_field] = agents[data[key_field]]
123
+ update_key(data["child"], key_field, agents)
124
+ # Recursively process all values in the dictionary
125
+ # for key, value in data.items():
126
+ # update_key(value, key_field, agents)
127
+ elif isinstance(data, list):
128
+ # If the current item is a list, recursively process each item in the list
129
+ for item in data:
130
+ update_key(item, key_field, agents)
131
+
132
+ print("[CHAIN PREV]", chains)
133
+
134
+ update_key(chains, "agent", agents)
135
+
136
+ print("[CHAIN NEW]", chains)
137
+
138
+ graph = build_chain(chains, checkpointer)
139
+
140
+ # for start_point in chains:
141
+ # builder.add_edge(START, start_point["id"])
142
+
143
+ # graph = builder.compile(checkpointer=checkpointer)
144
+
145
+ if not projects_chains.get(project_id):
146
+ projects_chains[project_id] = {id: graph}
147
+ else:
148
+ projects_chains[project_id].update({id: graph})
149
+
150
+ # if projects_chains.get(project_id):
151
+ # projects_chains.append(graph)
152
+ # else:
153
+ # projects_chains[project_id] = [graph]
154
+
155
+ # data_dict["created_at"] = current_time
156
+ # data_dict["updated_at"] = current_time
157
+ # data_dict["deleted_at"] = None
158
+ return {"message": "Chain created", "data": chains}
159
+
160
+
161
+ @app.get("/{project_id}/{id}/run")
162
+ def run(project_id: str, id: str, session_id:str, message:str="Hello world!"):
163
+ chain = projects_chains.get(project_id)
164
+ graph = None
165
+
166
+ if chain:
167
+ graph = chain.get(id)
168
+
169
+ if graph == None:
170
+ agent = projects_agents.get(project_id)
171
+ if agent:
172
+ runnable = agent.get(id)
173
+ # print("[AGENT]", runnable)
174
+ chains = [{
175
+ "id":"123",
176
+ "agent":runnable,
177
+ "checkpoint":False,
178
+ "condition":None,
179
+ "condition_from":None,
180
+ "child":[]
181
+ }]
182
+
183
+
184
+ graph = build_chain(chains, agent, checkpointer)
185
+
186
+ # for start_point in chains:
187
+ # builder.add_edge(START, start_point["id"])
188
+
189
+ # graph = builder.compile(checkpointer=checkpointer)
190
+
191
+ print("[GRAPH]", graph)
192
+
193
+ # agent_builder(chain={}, row=0, depth=0)
194
+ else:
195
+ return {"Error":"Agent or Chain does not exists!", "status_code":401}
196
+
197
+
198
+ # print("[NODES]", builder.nodes)
199
+ # print("[EDGES]", builder.edges)
200
+
201
+ # for chunk in graph.stream(
202
+ # {"messages": [HumanMessage(message)]},
203
+ # {"configurable": {"thread_id": session_id}},
204
+ # stream_mode="values",
205
+ # ):
206
+ # print("[CHUNK]", chunk)
207
+
208
+ out = graph.invoke(
209
+ {
210
+ "messages": [HumanMessage(message)],
211
+ "variables": {"user_input":message}
212
+ },
213
+ {"configurable": {"thread_id": session_id}}
214
+ )
215
+ return {"results":out["messages"][-1].content}
216
+
217
+
218
+ @app.get("/agents")
219
+ def get_agents():
220
+ return {"results":projects_agents}
221
+
222
+ @app.get("/")
223
+ def greet_json():
224
+ return {"Hello": "World!"}
chains.py ADDED
@@ -0,0 +1,468 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from typing import Annotated, Literal
2
+ from typing_extensions import TypedDict
3
+
4
+ from langgraph.graph import StateGraph, MessagesState, START, END
5
+ from langchain_core.messages import AIMessage, HumanMessage, SystemMessage
6
+ from langchain_core.output_parsers import JsonOutputParser
7
+ from langchain_community.document_transformers import BeautifulSoupTransformer, beautiful_soup_transformer
8
+
9
+ from langgraph.types import Command
10
+
11
+ from langchain_groq import ChatGroq
12
+
13
+ import operator
14
+ import pprint
15
+ import os
16
+ import requests
17
+ import html2text
18
+
19
+ API_KEY = os.getenv("GROQ_API_KEY")
20
+ OUT_RES = "<|FINISHED|>"
21
+
22
+ HTML_TRANSFORMER = html2text.HTML2Text()
23
+ HTML_TRANSFORMER.ignore_links = True
24
+ HTML_TRANSFORMER.ignore_images = True
25
+
26
+ BS_TRANSFORMER = BeautifulSoupTransformer()
27
+
28
+
29
+ def local_message_add(dict1, dict2):
30
+ key2 = list(dict2.keys())[0]
31
+ if key2 not in dict1:
32
+ dict1[key2] = dict2[key2]
33
+ else:
34
+ dict1[key2] = dict1[key2] + dict2[key2]
35
+ return dict1
36
+
37
+ def variable_state_update(dict1, dict2):
38
+ dict1.update(dict2)
39
+ return dict1
40
+
41
+ class GeneralStates(TypedDict):
42
+ messages: Annotated[list[dict[str, str]], lambda x,y:x+y]
43
+ checkpoints: dict[str,list]
44
+ local_messages: Annotated[dict, local_message_add]
45
+ variables: Annotated[dict, variable_state_update]
46
+
47
+
48
+ def format_sequence(seq, nested=False):
49
+ if isinstance(seq, (list, tuple, set, frozenset, dict)):# and not isinstance(seq, str):
50
+ # Handle dictionaries
51
+ if isinstance(seq, dict):
52
+ return format_dict(seq, nested=nested)
53
+ # Handle lists, tuples, sets, and frozensets
54
+ else:
55
+ return format_list_like(seq, nested=nested)
56
+ else:
57
+ return seq
58
+ # else:
59
+ # raise TypeError(f"Input must be a sequence (list, tuple, set, frozenset, dict, string), not a {type(seq)}")
60
+
61
+ def format_dict(d, nested=False):
62
+ # Format dictionary without enclosing braces
63
+ items = []
64
+ for i, (key, value) in enumerate(d.items()):
65
+ if isinstance(value, (list, tuple, set, frozenset, dict)):
66
+ value = format_sequence(value, nested=True) # Recursively format nested sequences
67
+ if not nested:
68
+ items.append(f"{i+1}. {key}: {value}")
69
+ else:
70
+ items.append(f"{key}: {value}")
71
+ return ",\n".join(items)
72
+
73
+ def format_list_like(seq, nested=False):
74
+ # Format list-like objects without enclosing brackets/parentheses
75
+ items = []
76
+ for i,item in enumerate(seq):
77
+ if isinstance(item, (list, tuple, set, frozenset, dict)):
78
+ item = format_sequence(item, nested=True) # Recursively format nested sequences
79
+ if not nested:
80
+ items.append(f"{i+1}. {item}")
81
+ else:
82
+ items.append(str(item))
83
+ return ",\n".join(items)
84
+
85
+
86
+ def format_dict_api(input_dict, combined):
87
+ formatted_dict = {}
88
+ for key, value in input_dict.items():
89
+ if isinstance(value, dict):
90
+ formatted_dict[key] = format_dict_api(value, combined)
91
+ elif isinstance(value, str):
92
+ # try:
93
+ formatted_dict[key] = value.format(**combined)
94
+ # except KeyError as e:
95
+ # print(f"Warning: Key {e} not found in combined dictionary for string {value}. Skipping formatting for this string.")
96
+ # formatted_dict[key] = value # keep original string if key not found
97
+
98
+ else:
99
+ formatted_dict[key] = value
100
+
101
+ return formatted_dict
102
+
103
+
104
+ def run_api(api_endpoints, variables, response, input_message, chain_id):
105
+ if not api_endpoints:
106
+ return {}
107
+ combined = variables.copy()
108
+ if response:
109
+ api_endpoint_type = "output"
110
+ if isinstance(response, dict):
111
+ combined = combined | response
112
+ # combined |= response
113
+ else:
114
+ combined["output_message"] = response
115
+ else:
116
+ api_endpoint_type = "input"
117
+
118
+ combined["input_message"] = input_message
119
+ resp = []
120
+ errors = []
121
+ for x in api_endpoints:
122
+ try:
123
+ input_var = {inp: combined[inp] for inp in x["input_variables"]}
124
+ res = requests.request(
125
+ x['method'],
126
+ x['url'],
127
+ headers = format_dict_api(x['headers'], input_var) if x["headers"] else None,
128
+ params = format_dict_api(x["params"], input_var) if x["params"] else None,
129
+ json = format_dict_api(x["request_body"], input_var) if x["request_body"] else None,
130
+ )
131
+
132
+ if x['response_type'] == 'json':
133
+ res = res.json()
134
+ else:
135
+ res = res.text
136
+
137
+ if res[:15] == "<!DOCTYPE html>":
138
+ if x["html_to_markdown"]:
139
+ res = HTML_TRANSFORMER.handle(res)
140
+ elif x["html_tags_to_extract"]:
141
+ res = BS_TRANSFORMER.extract_tags(res, tags=x["html_tags_to_extract"])
142
+ resp.append([res, x["name"]])
143
+ except Exception as e:
144
+ errors.append([e, x["name"]])
145
+
146
+ api_dict = {}
147
+ # if resp:
148
+ for x in resp:
149
+ # api_dict[f"<|{api_endpoint_type}_API_SUCCESS_{chain_id}_{x[1]}|>"] = x[0]
150
+ api_dict[f"{api_endpoint_type}_{x[1]}_{chain_id}_success"] = x[0]
151
+ for x in errors:
152
+ # api_dict[f"<|{api_endpoint_type}_API_ERROR_{chain_id}_{x[1]}|>"] = x[0]
153
+ api_dict[f"{api_endpoint_type}_{x[1]}_{chain_id}_error"] = x[0]
154
+ variables.update(api_dict)
155
+ return api_dict
156
+
157
+
158
+ def agent_builder(states: GeneralStates, chain: dict, row:int, depth: int):
159
+ # print("[BUILD AGENT] Start....")
160
+ # agent = chain.get("agent")
161
+ model_config = chain.get("agent")
162
+ print("[MODEL CONFIG]", model_config)
163
+ child = chain.get("child")
164
+ checkpoints = states.get("checkpoints", {})
165
+
166
+ print("[STATES]", states)
167
+
168
+ for k,v in checkpoints.items():
169
+ # if k == model_config["name"]:
170
+ if k == chain["id"]:
171
+ return Command(goto=v)
172
+
173
+ api_dict = {"variables":{}}
174
+
175
+ variables = states.get("variables", {})
176
+ variables["input_message"] = states["messages"][-1].content
177
+
178
+ # print("[CHAIN]", chain),
179
+ # print()
180
+
181
+ api_res = run_api(model_config["input_api_endpoints"], variables, None, states["messages"][-1].content, chain["id"])
182
+ api_dict["variables"].update(api_res)
183
+
184
+ for c in child:
185
+ if c["condition_from"] == "input" and states['messages'][-1].content.strip() == c["condition"]:
186
+ redirect_agent_message = AIMessage(f"Switch to Agent {c['id']}")
187
+
188
+ # local_message = states["local_messages"].get(model_config["name"])
189
+ local_message = states["local_messages"].get(chain["id"])
190
+ if local_message:
191
+ update_dict = {
192
+ "local_messages": {
193
+ # model_config["name"]:[redirect_agent_message],
194
+ chain["id"]:[redirect_agent_message],
195
+ c["id"]:[states['messages'][-1]]
196
+ },
197
+ }
198
+ else:
199
+ update_dict = {}
200
+
201
+ if c.get("checkpoint"):
202
+ # update_dict["checkpoints"] = {model_config["name"]:c["id"]}
203
+ update_dict["checkpoints"] = {chain["id"]:c["id"]}
204
+
205
+ return Command(goto=c["id"], update=update_dict | api_dict)
206
+
207
+ # messages = states["local_messages"].get(model_config["name"])
208
+ messages = states["local_messages"].get(chain["id"])
209
+
210
+ if messages:
211
+ messages.append(states["messages"][-1])
212
+ else:
213
+ messages = states["messages"]
214
+
215
+ input_var = model_config.get("input_variables")
216
+ output_variables = model_config.get("output_variables")
217
+
218
+
219
+ if model_config.get("is_template"):
220
+ response = model_config.get("prompt")
221
+ if input_var:
222
+ # response = response.format(**{var: format_sequence(variables[var]) for var in input_var})
223
+ response = response.format(**{var: variables[var] for var in input_var})
224
+ response = AIMessage(response)
225
+
226
+ api_res = run_api(model_config["output_api_endpoints"], variables, response.content, messages[-1].content, chain["id"])
227
+ api_dict["variables"].update(api_res)
228
+ # return {"messages":[response], "local_messages":{model_config["name"]:[response]}}
229
+
230
+ if output_variables:
231
+ out = {out_var: response.content for out_var in output_variables}
232
+ if "messages" not in output_variables:
233
+ api_dict["variables"].update(out)
234
+ return api_dict
235
+ else:
236
+ out.pop("messages")
237
+ api_dict["variables"].update(out)
238
+
239
+ return {"messages":[response], "local_messages":{chain["id"]:[response]}} | api_dict
240
+
241
+ def run_agent(i, loop_input_variables, variables):
242
+ if input_var:
243
+ print("[AGENT ID]", chain['id'])
244
+ print("[INPUT VARIABLES]", input_var)
245
+ print("[VARIABLES]", variables)
246
+ user_input = "\n".join([str(variables[var]) for var in input_var])
247
+ # print("[USER INPUT]", user_input)
248
+ if i == -1:
249
+ prompt = model_config.get("prompt").format(**{var: variables[var] for var in input_var})
250
+ else:
251
+ prompt = model_config.get("prompt").format(
252
+ **{var: variables[var][i] if var in loop_input_variables else variables[var] for var in input_var}
253
+ )
254
+ else:
255
+ user_input = messages[-1].content
256
+ prompt = model_config.get("prompt") + "\n\n" + messages[-1].content
257
+
258
+
259
+ model = ChatGroq(
260
+ # model="mixtral-8x7b-32768",
261
+ # model="llama-3.2-11b-vision-preview",
262
+ # model="llama-3.1-8b-instant",
263
+ # model = "gemma2-9b-it",
264
+ model="llama-3.3-70b-versatile",
265
+ # model="deepseek-r1-distill-llama-70b",
266
+ temperature=model_config.get("creativity"),
267
+ max_tokens=None,
268
+ timeout=None,
269
+ max_retries=2,
270
+ api_key=API_KEY
271
+ )
272
+
273
+ routes = model_config.get("routes")
274
+ output_collector = model_config.get("output_collector")
275
+
276
+ if routes:
277
+ add_prompt = f"YOU MUST GENERATE OUTPUT STRICTLY one of the following list : [{', '.join(routes)}]\n\n"
278
+ if model_config.get("routes_description"):
279
+ add_prompt += "HERE IS THE CONDITIONS FOR EACH OUTPUT:\n"
280
+ add_prompt += "\n".join([f"{x}: {y}" for x,y in zip(routes, model_config.get("routes_description"))])
281
+ add_prompt += "\n\n"
282
+
283
+ prompt = add_prompt + prompt
284
+ elif output_collector:
285
+ # CLOSING AND OPENING BRACKETS
286
+ add_prompt = f"YOU MUST GENERATE OUTPUT STRICTLY IN THE FOLLOWING JSON FORMAT, REMEMBER TO ADD {{}} BEFORE AND AFTER JSON CODE:\n"
287
+ # add_prompt += "\n".join([f"{x['name']}: {x['data_type']} = {x['description']}" for x in output_collector])
288
+ add_prompt += "\n".join(output_collector)
289
+ add_prompt += "\n\n"
290
+
291
+ prompt = prompt +"\n\n"+ add_prompt
292
+
293
+ response = (model | JsonOutputParser()).invoke(messages[:-1] + [HumanMessage(content=prompt)])
294
+
295
+ if output_variables:
296
+ for k in response.keys():
297
+ if k not in output_variables:
298
+ del response[k]
299
+
300
+ api_res = run_api(model_config["output_api_endpoints"], variables, response, messages[-1].content, chain["id"])
301
+
302
+ api_dict["variables"].update(api_res)
303
+
304
+ return {"variables":response | api_dict["variables"]}
305
+
306
+ response = model.invoke(messages[:-1] + [HumanMessage(content=prompt)])
307
+
308
+ for c in child:
309
+ if c["condition_from"] == "output" and response.content.strip() == c["condition"]:
310
+ redirect_agent_message = AIMessage(f"Switch to Agent {c['id']}")
311
+
312
+ # local_message = states["local_messages"].get(model_config["name"])
313
+ local_message = states["local_messages"].get(chain["id"])
314
+ if local_message:
315
+ update_dict = {
316
+ "local_messages": {
317
+ # model_config["name"]:[redirect_agent_message],
318
+ chain["id"]:[redirect_agent_message],
319
+ c["id"]:[HumanMessage(user_input)]
320
+ },
321
+ }
322
+ else:
323
+ update_dict = {}
324
+
325
+ if c.get("checkpoint"):
326
+ # update_dict["checkpoints"] = {model_config["name"]:c["id"]}
327
+ update_dict["checkpoints"] = {chain["id"]:c["id"]}
328
+
329
+ api_res = run_api(model_config["output_api_endpoints"], variables, response.content, messages[-1].content, chain["id"])
330
+ api_dict["variables"].update(api_res)
331
+
332
+ # if output_variables:
333
+ # api_dict["variables"].update({out_var: response.content for out_var in output_variables})
334
+
335
+ if output_variables:
336
+ out = {out_var: response.content for out_var in output_variables}
337
+ if "messages" not in output_variables:
338
+ api_dict["variables"].update(out)
339
+ return api_dict
340
+ else:
341
+ api_dict["messages"] = out.pop("messages")
342
+ api_dict["variables"].update(out)
343
+
344
+ return Command(goto=c["id"], update=update_dict | api_dict)
345
+ elif response.content.strip() == OUT_RES:
346
+
347
+ api_res = run_api(model_config["output_api_endpoints"], variables, None, messages[-1].content, chain["id"])
348
+ api_dict["variables"].update(api_res)
349
+
350
+ return {} | api_dict
351
+
352
+ api_res = run_api(model_config["output_api_endpoints"], variables, response.content, messages[-1].content, chain["id"])
353
+ api_dict["variables"].update(api_res)
354
+
355
+ if output_variables:
356
+ out = {out_var: response.content for out_var in output_variables}
357
+ if "messages" not in output_variables:
358
+ api_dict["variables"].update(out)
359
+ return api_dict
360
+ else:
361
+ out.pop("messages")
362
+ api_dict["variables"].update(out)
363
+
364
+
365
+ # return {"messages":[response], "local_messages":{model_config["name"]:[response]}} | api_dict
366
+ return {"messages":[response], "local_messages":{chain["id"]:[response]}} | api_dict
367
+
368
+ if not chain["loop_input_variables"]:
369
+ return run_agent(-1, [], variables)
370
+ else:
371
+ max_loop = min([len(states["variables"].get(x)) for x in chain["loop_input_variables"]])
372
+
373
+ updates = {"variables":{}}
374
+
375
+ for i in range(max_loop):
376
+ out_variables = run_agent(i, chain["loop_input_variables"], variables)
377
+
378
+ if type(out_variables) == dict:
379
+ if not out_variables.get("variables"):
380
+ continue
381
+ for k,v in out_variables["variables"].items():
382
+ if k not in updates["variables"].keys():
383
+ updates["variables"][k] = []
384
+ if type(v) == list:
385
+ updates["variables"][k] += v
386
+ else:
387
+ updates["variables"][k].append(v)
388
+ else:
389
+ updates = out_variables
390
+ return updates
391
+
392
+
393
+ def route(states, routes):
394
+ if states["messages"][-1].content.strip() in routes:
395
+ return states["messages"][-1].content.strip()
396
+ return END
397
+
398
+ def build_chain(chains, checkpointer, parent_name=None, depth=0):
399
+ print("[BUILD CHAIN] START....")
400
+
401
+ stack = [(chains, parent_name, depth, 0)]
402
+
403
+ builder = StateGraph(GeneralStates)
404
+
405
+ while stack:
406
+ current_chains, current_parent, current_depth, i = stack.pop()
407
+ print("STACK", i)
408
+
409
+ if i >= len(current_chains):
410
+ continue
411
+
412
+ c = current_chains[i]
413
+ c_id = c["id"]
414
+
415
+ # agent = agents[c["agent"]]
416
+
417
+ try:
418
+ print("ADDED NODE!")
419
+ builder.add_node(
420
+ c_id,
421
+ lambda states, c=c, i=i, depth=current_depth: agent_builder(states, c, i, depth)
422
+ )
423
+ # print("[ADD NODE]", c_id, i, current_depth)
424
+ except ValueError as e:
425
+ print("[ERROR]",e)
426
+ pass
427
+
428
+
429
+ # Push the next chain in the current list to be processed after children
430
+ if i + 1 < len(current_chains):
431
+ stack.append((current_chains, current_parent, current_depth, i + 1))
432
+
433
+ # Process children or add edge to END
434
+ if c.get("child"):
435
+ stack.append((
436
+ c["child"],
437
+ c_id,
438
+ current_depth + 1,
439
+ 0
440
+ ))
441
+
442
+ condition_ids = []
443
+
444
+ for x in c["child"]:
445
+ if x["condition"]:
446
+ condition_ids.append(x["id"])
447
+ else:
448
+ builder.add_edge(c_id, x["id"])
449
+
450
+ if condition_ids:
451
+ builder.add_conditional_edges(
452
+ c_id,
453
+ lambda states: route(states, condition_ids), path_map=condition_ids + [END]
454
+ )
455
+ else:
456
+ builder.add_edge(
457
+ c_id,
458
+ END
459
+ )
460
+
461
+ print("SET STARTING POINTS")
462
+
463
+ for start_point in chains:
464
+ builder.add_edge(START, start_point["id"])
465
+ print("[NODES]", builder.nodes)
466
+ print("[EDGES]", builder.edges)
467
+ graph = builder.compile(checkpointer=checkpointer)
468
+ return graph
models.py ADDED
@@ -0,0 +1,53 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from uuid import UUID
2
+ from datetime import datetime
3
+ from typing import Optional, List
4
+ from pydantic import BaseModel, Field
5
+
6
+ class Agent(BaseModel):
7
+ id: str
8
+ name: str
9
+ project_id: str
10
+ input_variables: Optional[List[str]]
11
+ prompt: str
12
+ is_template: Optional[bool]
13
+ routes: Optional[List[str]]
14
+ routes_description: Optional[List[str]]
15
+ output_collector: Optional[str]
16
+
17
+ output_variables: Optional[List[str]]
18
+ input_api_endpoints: Optional[List[str]]
19
+ output_api_endpoints: Optional[List[str]]
20
+
21
+ conversation_style: float = Field(..., ge=-9.9, le=9.9)
22
+ creativity: float = Field(..., ge=0.0, le=1.0)
23
+ fallback_message: str
24
+ fallback_threshold: float
25
+
26
+
27
+ class OutputCollector(BaseModel):
28
+ id: str
29
+ project_id: str
30
+ keys: List[str]
31
+ descriptions: List[str]
32
+ data_types: List[str]
33
+
34
+
35
+ class APIEndpoint(BaseModel):
36
+ id: str
37
+ project_id: str
38
+ name: str
39
+ description: Optional[str]
40
+
41
+ input_variables: Optional[list]
42
+
43
+ url: str
44
+ method: str
45
+ headers: Optional[dict]
46
+ params: Optional[dict]
47
+ request_body: Optional[dict]
48
+
49
+ html_to_markdown: Optional[bool]
50
+ html_tags_to_extract: Optional[list]
51
+
52
+ response_type: Optional[str]
53
+
requirements.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ fastapi
2
+ uvicorn[standard]
3
+ langgraph
4
+ langchain-groq