LKTs commited on
Commit
ba71fca
·
verified ·
1 Parent(s): 19d45f6

Upload 65 files

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +1 -35
  2. README.md +182 -14
  3. agents/__init__.py +4 -0
  4. agents/__pycache__/__init__.cpython-312.pyc +0 -0
  5. agents/__pycache__/aggregator_agent.cpython-312.pyc +0 -0
  6. agents/__pycache__/base.cpython-312.pyc +0 -0
  7. agents/__pycache__/cafe_bot.cpython-312.pyc +0 -0
  8. agents/__pycache__/database_agent.cpython-312.pyc +0 -0
  9. agents/__pycache__/intake_agent.cpython-312.pyc +0 -0
  10. agents/__pycache__/test_agents.cpython-312.pyc +0 -0
  11. agents/aggregator_agent.py +66 -0
  12. agents/base.py +21 -0
  13. agents/cafe_bot.py +49 -0
  14. agents/database_agent.py +117 -0
  15. agents/intake_agent.py +72 -0
  16. app.py +7 -64
  17. assets/__init__.py +1 -0
  18. assets/knowledge_base/landscape_cafe/landscape_cafe_contact.md +49 -0
  19. assets/knowledge_base/landscape_cafe/landscape_cafe_menu.md +49 -0
  20. assets/knowledge_base/landscape_cafe/landscape_cafe_overview.md +35 -0
  21. assets/knowledge_base/landscape_cafe/landscape_cafe_reviews_and_tips.md +50 -0
  22. assets/knowledge_base/promotion/landscape_cafe_promos_august.md +41 -0
  23. assets/knowledge_base/promotion/landscape_cafe_promos_july.md +41 -0
  24. assets/raw_data/customer.csv +7 -0
  25. assets/raw_data/menu_list.csv +16 -0
  26. assets/raw_data/order_items.csv +16 -0
  27. assets/raw_data/orders.csv +7 -0
  28. assets/raw_data/promotions.csv +6 -0
  29. assets/raw_data/reservations.csv +4 -0
  30. assets/raw_data/staff.csv +6 -0
  31. assets/raw_data/table_status.csv +6 -0
  32. config/__init__.py +4 -0
  33. config/__pycache__/__init__.cpython-312.pyc +0 -0
  34. config/__pycache__/agent_registry.cpython-312.pyc +0 -0
  35. config/__pycache__/config.cpython-312.pyc +0 -0
  36. config/__pycache__/settings.cpython-312.pyc +0 -0
  37. config/__pycache__/ui_config.cpython-312.pyc +0 -0
  38. config/agent_registry.py +13 -0
  39. config/config.py +100 -0
  40. config/settings.py +32 -0
  41. config/ui_config.py +25 -0
  42. database/__init__.py +4 -0
  43. database/__pycache__/__init__.cpython-312.pyc +0 -0
  44. database/__pycache__/create_db.cpython-312.pyc +0 -0
  45. database/create_db.py +48 -0
  46. database/database.db +0 -0
  47. main.py +103 -0
  48. rag/__init__.py +4 -0
  49. rag/__pycache__/__init__.cpython-312.pyc +0 -0
  50. rag/__pycache__/rag_system.cpython-312.pyc +0 -0
.gitattributes CHANGED
@@ -1,35 +1 @@
1
- *.7z filter=lfs diff=lfs merge=lfs -text
2
- *.arrow filter=lfs diff=lfs merge=lfs -text
3
- *.bin filter=lfs diff=lfs merge=lfs -text
4
- *.bz2 filter=lfs diff=lfs merge=lfs -text
5
- *.ckpt filter=lfs diff=lfs merge=lfs -text
6
- *.ftz filter=lfs diff=lfs merge=lfs -text
7
- *.gz filter=lfs diff=lfs merge=lfs -text
8
- *.h5 filter=lfs diff=lfs merge=lfs -text
9
- *.joblib filter=lfs diff=lfs merge=lfs -text
10
- *.lfs.* filter=lfs diff=lfs merge=lfs -text
11
- *.mlmodel filter=lfs diff=lfs merge=lfs -text
12
- *.model filter=lfs diff=lfs merge=lfs -text
13
- *.msgpack filter=lfs diff=lfs merge=lfs -text
14
- *.npy filter=lfs diff=lfs merge=lfs -text
15
- *.npz filter=lfs diff=lfs merge=lfs -text
16
- *.onnx filter=lfs diff=lfs merge=lfs -text
17
- *.ot filter=lfs diff=lfs merge=lfs -text
18
- *.parquet filter=lfs diff=lfs merge=lfs -text
19
- *.pb filter=lfs diff=lfs merge=lfs -text
20
- *.pickle filter=lfs diff=lfs merge=lfs -text
21
- *.pkl filter=lfs diff=lfs merge=lfs -text
22
- *.pt filter=lfs diff=lfs merge=lfs -text
23
- *.pth filter=lfs diff=lfs merge=lfs -text
24
- *.rar filter=lfs diff=lfs merge=lfs -text
25
- *.safetensors filter=lfs diff=lfs merge=lfs -text
26
- saved_model/**/* filter=lfs diff=lfs merge=lfs -text
27
- *.tar.* filter=lfs diff=lfs merge=lfs -text
28
- *.tar filter=lfs diff=lfs merge=lfs -text
29
- *.tflite filter=lfs diff=lfs merge=lfs -text
30
- *.tgz filter=lfs diff=lfs merge=lfs -text
31
- *.wasm filter=lfs diff=lfs merge=lfs -text
32
- *.xz filter=lfs diff=lfs merge=lfs -text
33
- *.zip filter=lfs diff=lfs merge=lfs -text
34
- *.zst filter=lfs diff=lfs merge=lfs -text
35
- *tfevents* filter=lfs diff=lfs merge=lfs -text
 
1
+ *.bin filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
README.md CHANGED
@@ -1,14 +1,182 @@
1
- ---
2
- title: CafeAgentX
3
- emoji: 💬
4
- colorFrom: yellow
5
- colorTo: purple
6
- sdk: gradio
7
- sdk_version: 5.0.1
8
- app_file: app.py
9
- pinned: false
10
- license: cc-by-nc-nd-4.0
11
- short_description: CafeAgentX is an AI-powered multi-agent platform for cafes a
12
- ---
13
-
14
- An example chatbot using [Gradio](https://gradio.app), [`huggingface_hub`](https://huggingface.co/docs/huggingface_hub/v0.22.2/en/index), and the [Hugging Face Inference API](https://huggingface.co/docs/api-inference/index).
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # SupportFlowX 🚀
2
+ *Agent Orchestration Framework for Scalable, Modular Chatbots*
3
+
4
+ [**🌐 Try the CafeAgentX on Hugging Face Spaces**](https://huggingface.co/spaces/LKTs/CafeAgentX)
5
+
6
+ > **Example Project:** CafeAgentX ☕ – Café Assistant Demo
7
+
8
+ <img width="1278" height="854" alt="image" src="https://github.com/user-attachments/assets/c215c19d-da07-4b50-b961-14a1eed8c607" />
9
+
10
+
11
+ ---
12
+
13
+ ## Overview
14
+
15
+ **SupportFlowX** is a flexible, agentic orchestration framework designed to build scalable, multi-agent AI assistants.
16
+ The core design enables easy integration, scaling, and replacement of domain-specific AI agents for any business scenario.
17
+ By orchestrating tasks between specialized agents, SupportFlowX minimizes bottlenecks and maximizes both reliability and maintainability.
18
+
19
+ **CafeAgentX** is provided as a working demo — a café chatbot that showcases agent collaboration for customer Q&A, menu, promotions, and database support.
20
+
21
+ ---
22
+
23
+ ## ✨ Key Features
24
+
25
+ - **Orchestration-First:** True agentic routing; intake agent delegates tasks to skill-specific agents.
26
+ - **Highly Scalable:** Add or swap agents and domains with minimal code changes.
27
+ - **Reduce LLM Bottlenecks:** Parallel and specialized task handling for better throughput.
28
+ - **Production-Ready Demo:** Café Assistant (CafeAgentX) demonstrates end-to-end deployment.
29
+ - **Gradio UI:** Easy, ready-to-use chat interface for fast prototyping or real-world service.
30
+
31
+ <img width="446" height="456" alt="image" src="https://github.com/user-attachments/assets/cb515f28-4cbd-4c50-8914-ea4855324050" />
32
+
33
+ </details>
34
+
35
+ ---
36
+
37
+ ## 💡 Use Cases
38
+
39
+ SupportFlowX is not limited to cafés!
40
+ - Customer support (retail, banking, IT helpdesk, etc.)
41
+ - Booking & reservations
42
+ - HR or internal knowledge bots
43
+ - Medical, law, or domain-specific assistants
44
+ - … any scenario where orchestrated, scalable agents make sense.
45
+
46
+ **Just swap out the agent modules and data!**
47
+
48
+ ---
49
+
50
+ ## 🏗️ Architecture
51
+
52
+ The core logic is in `core.py`, which handles orchestration and routing between specialized agents.
53
+ - All main configuration is in `config.py`.
54
+ - Database files for both structured data and RAG are inside `data/` and `knowledge-base/`.
55
+ - Each agent is modular and extensible (see `AG00_*`, `AG01_*`, ...).
56
+
57
+ ## Project Structure
58
+
59
+ ```
60
+
61
+ - agents/ — Agent logic and wrappers
62
+ - assets/ — Images, knowledge base, raw data
63
+ - config/ — Configuration and agent registry
64
+ - database/ — SQLite DB and scripts
65
+ - rag/ — RAG system and embeddings
66
+ - ui/ — Gradio UI and themes
67
+ - workflows/ — Workflow graph and routing
68
+
69
+ ```
70
+
71
+ ---
72
+
73
+ ## 🚀 Quick Start
74
+
75
+ ### 1. Clone the repo
76
+
77
+ ```bash
78
+ git clone https://github.com/your-username/supportflowx.git
79
+ cd supportflowx
80
+ ````
81
+
82
+ ### 2. Install dependencies
83
+
84
+ #### With pip
85
+
86
+ ```bash
87
+ pip install -r requirements.txt
88
+ ```
89
+
90
+ #### Or with uv (faster installs/locking)
91
+
92
+ ```bash
93
+ uv pip install -r requirements.txt
94
+ ```
95
+
96
+ ### 3. Prepare data & API key
97
+
98
+ * Place your knowledge base (RAG documents) in `/knowledge-base`
99
+ * Place your sample DB as `/data/database.db` (or as configured)
100
+ * **Get a Gemini API Key**: [https://makersuite.google.com/app/apikey](https://makersuite.google.com/app/apikey) (for Google Generative AI access)
101
+
102
+ ### 4. Run the Demo App
103
+
104
+ ```bash
105
+ python core.py
106
+ ```
107
+
108
+ Then open the local Gradio UI in your browser!
109
+
110
+ ---
111
+
112
+ ## 🛠️ Customization & Scaling
113
+
114
+ * **Add new skills:** Create new agent modules (see AGXX\_\*.py), import and register in `core.py`.
115
+ * **Change business logic:** Tweak agent routing and orchestration logic as needed.
116
+ * **Swap domains:** Replace Café agents and data with your own (e.g. legal, retail, travel, etc.)
117
+ * **Production Deployment:** Wrap in FastAPI/ASGI and run with `uvicorn` for robust serving.
118
+
119
+ ---
120
+
121
+ ## 🖥️ Example: CafeAgentX
122
+
123
+ CafeAgentX is a demo café assistant built with SupportFlowX, featuring:
124
+
125
+ * Menu, promotions, and table info via RAG & database queries
126
+ * Natural language chat with easy UI
127
+ * Realistic, extensible agent collaboration (IntakeAgent → CafeBot & DBAgent → Aggregator → Response)
128
+
129
+ > **Workflow Diagram:**
130
+ > *(See attached diagram / image in repo for orchestration flow)*
131
+
132
+ ---
133
+
134
+ ## 📦 Requirements
135
+
136
+ * Python 3.8+
137
+ * [gradio](https://gradio.app/)
138
+ * [langgraph](https://github.com/langchain-ai/langgraph)
139
+ * [typing-extensions](https://pypi.org/project/typing-extensions/)
140
+ * [operator](https://docs.python.org/3/library/operator.html)
141
+ * [langchain-google-genai](https://github.com/langchain-ai/langchain)
142
+ * [pillow](https://python-pillow.org/)
143
+ * [chromadb](https://www.trychroma.com/)
144
+ * [sentence-transformers](https://www.sbert.net/)
145
+ * [PyPDF2](https://pypdf2.readthedocs.io/)
146
+ * [pymupdf](https://pymupdf.readthedocs.io/)
147
+ * [langchain-text-splitters](https://python.langchain.com/docs/modules/data_connection/document_transformers/text_splitters/)
148
+ * [python-dotenv](https://pypi.org/project/python-dotenv/)
149
+ * [pandas](https://pandas.pydata.org/)
150
+ * [sqlite3](https://docs.python.org/3/library/sqlite3.html)
151
+
152
+ > All listed in `requirements.txt`
153
+
154
+ ---
155
+
156
+ ## 🔒 License
157
+
158
+ Copyright (c) 2024 Thai
159
+
160
+ This project ("SupportFlowX" and all included examples such as "CafeAgentX") is released for **educational, research, or internal evaluation purposes only**.
161
+
162
+ - **No commercial use.**
163
+ - **No redistribution, sublicensing, or use in proprietary software.**
164
+ - Modification for private/learning purposes is allowed.
165
+ - For any public deployment, commercial use, or redistribution, please contact the author for explicit written permission.
166
+
167
+ All rights reserved.
168
+
169
+ ## ⭐ Credits
170
+
171
+ Built with:
172
+
173
+ * [LangGraph](https://langchain-ai.github.io/langgraph/)
174
+ * [Gradio](https://gradio.app/)
175
+ * [Google Generative AI](https://makersuite.google.com/)
176
+
177
+ ---
178
+
179
+ **Ready to orchestrate your own AI support workflows?
180
+ Fork SupportFlowX, swap the agents, and build the next smart support system — with no single-agent bottleneck!**
181
+
182
+ ---
agents/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ # This file marks the 'agents' directory as a Python package.
2
+
3
+
4
+
agents/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (183 Bytes). View file
 
agents/__pycache__/aggregator_agent.cpython-312.pyc ADDED
Binary file (4.74 kB). View file
 
agents/__pycache__/base.cpython-312.pyc ADDED
Binary file (1.01 kB). View file
 
agents/__pycache__/cafe_bot.cpython-312.pyc ADDED
Binary file (3.61 kB). View file
 
agents/__pycache__/database_agent.cpython-312.pyc ADDED
Binary file (8.34 kB). View file
 
agents/__pycache__/intake_agent.cpython-312.pyc ADDED
Binary file (5 kB). View file
 
agents/__pycache__/test_agents.cpython-312.pyc ADDED
Binary file (2.53 kB). View file
 
agents/aggregator_agent.py ADDED
@@ -0,0 +1,66 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from agents.base import BaseAgent
2
+ from datetime import datetime
3
+
4
+ class AggregatorAgent(BaseAgent):
5
+ def __init__(self, gemini_agent):
6
+ self.gemini = gemini_agent
7
+
8
+ def summarize_database_output(self, table_text, user_message):
9
+ prompt = (
10
+ "You are a helpful and friendly assistant. Organize and present the following database table for the user in a clear, concise, and visually appealing summary. "
11
+ "Use bullet points, short tables, or lists if helpful, and add emojis to enhance readability where appropriate. "
12
+ "Focus only on answering the user's request; do not add extra information. "
13
+ "If the table is empty, reply with a polite message that no data was found. "
14
+ "Always answer in the exact same language as the user's question.\n\n"
15
+ f"User question: {user_message}\n"
16
+ f"Database output:\n{table_text}\n"
17
+ )
18
+ result = self.gemini.invoke([{"role": "user", "content": prompt}])
19
+ return result.content if hasattr(result, "content") else str(result)
20
+
21
+ def summarize_multiple_agents(self, agent_outputs, user_message):
22
+ prompt = (
23
+ "You are a helpful and friendly assistant. The following are responses from multiple support agents to the same user query. "
24
+ "Please combine, reorganize, and summarize all relevant information into a single, concise, and easy-to-read reply for the user. "
25
+ "Remove any duplicate or redundant details. Use a friendly tone, and you may add emojis to make the message more engaging. "
26
+ "If there are conflicting answers, use the most accurate or comprehensive information.\n\n"
27
+ "Always answer in the exact same language as the user's question.\n"
28
+ f"User question: {user_message}\n"
29
+ "Agent outputs:\n"
30
+ )
31
+ for label, text in agent_outputs:
32
+ prompt += f"[{label}]: {text}\n"
33
+ result = self.gemini.invoke([{"role": "user", "content": prompt}])
34
+ return result.content if hasattr(result, "content") else str(result)
35
+
36
+ def process(self, state: dict) -> dict:
37
+ assigned_agents = state.get("assigned_agents", [])
38
+ user_message = state.get("user_message", "")
39
+ log_msg = []
40
+ now = datetime.now().isoformat(timespec='seconds')
41
+ log_msg.append(f"[{now}] [AggregatorAgent] Processing assigned_agents: {[a['agent'] for a in assigned_agents]}")
42
+ waiting = [a["agent"] for a in assigned_agents if not a.get("result")]
43
+ if waiting:
44
+ log_msg.append(f"[{now}] [AggregatorAgent] Waiting for agents: {waiting}")
45
+ return {"logs": log_msg}
46
+ if len(assigned_agents) == 1:
47
+ agent = assigned_agents[0]["agent"]
48
+ result = assigned_agents[0].get("result", "")
49
+ if agent != "coffee_db_agent":
50
+ log_msg.append(f"[{now}] [AggregatorAgent] Single agent, passing through result.")
51
+ return {"final_response": result, "logs": log_msg}
52
+ summary = self.summarize_database_output(result, user_message)
53
+ log_msg.append(f"[{now}] [AggregatorAgent] Summarized database output.")
54
+ return {"final_response": summary, "logs": log_msg}
55
+ agent_outputs = []
56
+ for a in assigned_agents:
57
+ agent = a["agent"]
58
+ result = a.get("result", "")
59
+ if result:
60
+ agent_outputs.append((agent, result))
61
+ summary = self.summarize_multiple_agents(agent_outputs, user_message)
62
+ log_msg.append(f"[{now}] [AggregatorAgent] Summarized multi-agent output.")
63
+ return {"final_response": summary.strip(), "logs": log_msg}
64
+
65
+
66
+
agents/base.py ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from abc import ABC, abstractmethod
2
+ from typing import Dict
3
+
4
+ class BaseAgent(ABC):
5
+ """
6
+ Abstract base class for all agents in SupportFlowX.
7
+ Every agent must implement the process(state: dict) -> dict method.
8
+ """
9
+ @abstractmethod
10
+ def process(self, state: Dict) -> Dict:
11
+ """
12
+ Process the input state and return a result dict.
13
+ Args:
14
+ state (dict): The input state for the agent.
15
+ Returns:
16
+ dict: The output/result from the agent.
17
+ """
18
+ pass
19
+
20
+
21
+
agents/cafe_bot.py ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from agents.base import BaseAgent
2
+ from datetime import datetime
3
+
4
+ class LanscapeCafeBot(BaseAgent):
5
+ def __init__(self, rag_system, gemini_agent, system_prompt: str = None, top_k: int = 8):
6
+ self.rag = rag_system
7
+ self.gemini = gemini_agent
8
+ self.top_k = top_k
9
+ self.system_prompt = system_prompt or (
10
+ "You are a helpful product support AI.\n"
11
+ "- Answer user questions only using the reference context provided.\n"
12
+ "- If the answer is not in the context, tell user that you do not have enough information.\n"
13
+ "- Respond with clear and polite language. You may use emojis to enhance your response.\n"
14
+ "- Do not include opinions, further explanations, or follow-up questions in your replies.\n"
15
+ "- Always use the same language as the user."
16
+ )
17
+
18
+ def process(self, state: dict) -> dict:
19
+ question = ""
20
+ for a in state.get("assigned_agents", []):
21
+ if a["agent"] == "landscape_cafe_bot":
22
+ question = a["command"]
23
+ break
24
+ logs = []
25
+ now = datetime.now().isoformat(timespec='seconds')
26
+ logs.append(f"[{now}] [CafeBot] Received question: '{question}'")
27
+ try:
28
+ context = self.rag.query(question, top_k=self.top_k)
29
+ logs.append(f"[{now}] [CafeBot] RAG context retrieved. Context length: {len(context)}")
30
+ except Exception as e:
31
+ logs.append(f"[{now}] [CafeBot][ERROR] Failed to retrieve context: {e}")
32
+ return {"result": f"❌ Failed to retrieve reference context: {e}", "logs": logs}
33
+ user_prompt = f"Reference context:\n{context}\n\nUser question: {question}"
34
+ messages = [
35
+ {"role": "system", "content": self.system_prompt},
36
+ {"role": "user", "content": user_prompt}
37
+ ]
38
+ try:
39
+ answer = self.gemini.invoke(messages)
40
+ result = answer.content if hasattr(answer, "content") else str(answer)
41
+ logs.append(f"[{now}] [CafeBot] Gemini LLM returned answer. Length: {len(result)}")
42
+ except Exception as e:
43
+ logs.append(f"[{now}] [CafeBot][ERROR] Gemini LLM failed: {e}")
44
+ return {"result": f"❌ Gemini LLM Error: {e}", "logs": logs}
45
+ for a in state.get("assigned_agents", []):
46
+ if a["agent"] == "landscape_cafe_bot":
47
+ a["result"] = result
48
+ logs.append(f"[{now}] [CafeBot] Result set in assigned_agents.")
49
+ return {"logs": logs}
agents/database_agent.py ADDED
@@ -0,0 +1,117 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from agents.base import BaseAgent
2
+ import sqlite3
3
+ import pandas as pd
4
+ from datetime import datetime
5
+ import re
6
+
7
+ class CoffeeDatabaseAgent(BaseAgent):
8
+ def __init__(self, db_path: str, llm_agent):
9
+ self.db_path = db_path
10
+ self.llm_agent = llm_agent
11
+
12
+ def check_connection(self) -> bool:
13
+ try:
14
+ conn = sqlite3.connect(self.db_path)
15
+ conn.execute("SELECT 1;")
16
+ conn.close()
17
+ return True
18
+ except Exception as e:
19
+ print(f"[DatabaseAgent] DB Connection Error: {e}")
20
+ return False
21
+
22
+ def get_schema_overview(self) -> str:
23
+ try:
24
+ conn = sqlite3.connect(self.db_path)
25
+ cursor = conn.cursor()
26
+ tables = cursor.execute("SELECT name FROM sqlite_master WHERE type='table'").fetchall()
27
+ if not tables:
28
+ return "(No tables found)"
29
+ lines = []
30
+ for (table,) in tables:
31
+ columns = cursor.execute(f"PRAGMA table_info({table})").fetchall()
32
+ col_list = [col[1] for col in columns]
33
+ col_meta = []
34
+ for col in col_list:
35
+ if any(kw in col.lower() for kw in ["status", "type", "category", "state", "flag"]):
36
+ vals = cursor.execute(f"SELECT DISTINCT {col} FROM {table} LIMIT 21").fetchall()
37
+ vals = [str(v[0]) for v in vals if v[0] is not None]
38
+ if 1 < len(vals) <= 20:
39
+ col_meta.append(f"{col}: [{', '.join(vals)}]")
40
+ if col_meta:
41
+ lines.append(f"{table}({', '.join(col_list)}) # " + "; ".join(col_meta))
42
+ else:
43
+ lines.append(f"{table}({', '.join(col_list)})")
44
+ conn.close()
45
+ return "\n".join(lines)
46
+ except Exception as e:
47
+ return f"(Schema error: {e})"
48
+
49
+ def llm_to_sql(self, command: str, schema: str) -> str:
50
+ prompt = (
51
+ "You are a database specialist agent. Given the schema and user request, generate a single, safe SQL SELECT statement. "
52
+ "If you cannot answer with available tables/columns, reply NO_SQL.\n"
53
+ f"Database schema overview:\n{schema}\n"
54
+ f"User request: {command}\nSQL:"
55
+ )
56
+ response = self.llm_agent.invoke([{"role": "user", "content": prompt}])
57
+ if hasattr(response, 'content'):
58
+ sql = response.content
59
+ else:
60
+ sql = str(response)
61
+ match = re.search(r"```sql\s*(.*?)```", sql, re.DOTALL | re.IGNORECASE)
62
+ if match:
63
+ sql = match.group(1).strip()
64
+ elif sql.startswith("```") and sql.endswith("```"):
65
+ sql = sql[3:-3].strip()
66
+ elif sql.upper().startswith("SQL:"):
67
+ sql = sql[4:].strip()
68
+ return sql
69
+
70
+ def query_database(self, sql: str, max_rows: int = 15) -> pd.DataFrame:
71
+ if sql.upper() == "NO_SQL":
72
+ raise ValueError("Cannot answer this question with the given schema.")
73
+ conn = sqlite3.connect(self.db_path)
74
+ try:
75
+ df = pd.read_sql_query(sql, conn)
76
+ finally:
77
+ conn.close()
78
+ return df.head(max_rows)
79
+
80
+ def process(self, state: dict) -> dict:
81
+ command = ""
82
+ for a in state.get("assigned_agents", []):
83
+ if a["agent"] == "coffee_db_agent":
84
+ command = a["command"]
85
+ break
86
+ logs = []
87
+ now = datetime.now().isoformat(timespec='seconds')
88
+ logs.append(f"[{now}] [DatabaseAgent] Received command: '{command}'")
89
+ if not self.check_connection():
90
+ log = f"[{now}] [DatabaseAgent][ERROR] Cannot connect to database ({self.db_path})"
91
+ return {"result": "❌ Database connection failed.", "logs": [log]}
92
+ schema = self.get_schema_overview()
93
+ logs.append(f"[{now}] [DatabaseAgent] Schema overview: {schema}")
94
+ try:
95
+ sql = self.llm_to_sql(command, schema)
96
+ logs.append(f"[{now}] [DatabaseAgent] LLM generated SQL: {sql}")
97
+ except Exception as e:
98
+ log = f"[{now}] [DatabaseAgent][ERROR] LLM to SQL failed: {e}"
99
+ return {"result": f"❌ [LLM Error]: {e}", "logs": logs + [log]}
100
+ try:
101
+ df = self.query_database(sql)
102
+ if df.empty:
103
+ result = "No data found for your request."
104
+ else:
105
+ result = f"Query result:\n{df.to_string(index=False)}"
106
+ logs.append(f"[{now}] [DatabaseAgent] Query executed successfully. Rows: {len(df)}")
107
+ except Exception as e:
108
+ result = f"❌ [DB Error]: {e}\n(SQL: {sql})"
109
+ logs.append(f"[{now}] [DatabaseAgent][ERROR] Query execution failed: {e}")
110
+ logs.append(f"[{now}] [DatabaseAgent] Done. Result set in assigned_agents.")
111
+ for a in state.get("assigned_agents", []):
112
+ if a["agent"] == "coffee_db_agent":
113
+ a["result"] = result
114
+ return {"logs": logs}
115
+
116
+
117
+
agents/intake_agent.py ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from agents.base import BaseAgent
2
+ from datetime import datetime
3
+ from config.config import ALLOWED_AGENTS, INTAKE_PROMPT, RESPONSIBLITY
4
+
5
+ class IntakeAgent(BaseAgent):
6
+ def __init__(self, gemini_with_output, allowed_agents=None, intake_prompt=None):
7
+ self.gemini_with_output = gemini_with_output
8
+ self.allowed_agents = allowed_agents or ALLOWED_AGENTS
9
+ self.intake_prompt = intake_prompt or INTAKE_PROMPT
10
+
11
+ def build_messages(self, system_prompt: str, history: list, user_message: str) -> list:
12
+ messages = []
13
+ if system_prompt:
14
+ messages.append({"role": "system", "content": system_prompt.strip()})
15
+ if history:
16
+ messages.extend(history)
17
+ if user_message:
18
+ messages.append({"role": "user", "content": user_message.strip()})
19
+ return messages
20
+
21
+ def validate_assignments(self, assignments, allowed_agents):
22
+ errors = []
23
+ for a in assignments:
24
+ if a["agent"] not in allowed_agents:
25
+ errors.append(f"[IntakeAgent] Unknown agent assigned: {a['agent']}")
26
+ if errors:
27
+ raise ValueError("\n".join(errors))
28
+
29
+ @staticmethod
30
+ def get_agent_capabilities(allowed_agents):
31
+ return "\n".join([f"- {k}: {v['capability']}" for k, v in allowed_agents.items()])
32
+
33
+ def process(self, state: dict) -> dict:
34
+ now = datetime.now().strftime('%Y-%m-%d %H:%M:%S')
35
+ allowed_agents = self.allowed_agents
36
+ system_prompt = self.intake_prompt.format(
37
+ responsibility=RESPONSIBLITY,
38
+ Date_time=now,
39
+ get_agent_capabilities=self.get_agent_capabilities(allowed_agents),
40
+ agent_names=list(allowed_agents.keys())
41
+ )
42
+ user_message = state.get("user_message", "")
43
+ history = state.get("chat_history", [])
44
+ logs = []
45
+ logs.append(f"[{now}] [IntakeAgent] Received user message: '{user_message}'")
46
+ prompt = self.build_messages(system_prompt, history, user_message)
47
+ logs.append(f"[{now}] [IntakeAgent] Built prompt for LLM. History count: {len(history)}")
48
+ try:
49
+ gemini_result = self.gemini_with_output.invoke(prompt)
50
+ assignments = gemini_result.get("assignments", [])
51
+ logs.append(f"[{now}] [IntakeAgent] LLM returned assignments: {assignments}")
52
+ self.validate_assignments(assignments, allowed_agents)
53
+ except Exception as e:
54
+ logs.append(f"[{now}] [IntakeAgent][ERROR] LLM or assignment validation failed: {e}")
55
+ return {"assigned_agents": [], "final_response": "", "logs": logs}
56
+ is_finished = any(a.get("finish", False) for a in assignments)
57
+ final_response = ""
58
+ if is_finished:
59
+ for a in assignments:
60
+ if a.get("finish"):
61
+ final_response = a.get("command", "")
62
+ logs.append(f"[{now}] [IntakeAgent] Conversation finished. Final response: '{final_response}'")
63
+ else:
64
+ logs.append(f"[{now}] [IntakeAgent] Assignments sent to next agent(s): {[a['agent'] for a in assignments]}")
65
+ return {
66
+ "assigned_agents": assignments,
67
+ "final_response": final_response,
68
+ "logs": logs
69
+ }
70
+
71
+
72
+
app.py CHANGED
@@ -1,64 +1,7 @@
1
- import gradio as gr
2
- from huggingface_hub import InferenceClient
3
-
4
- """
5
- For more information on `huggingface_hub` Inference API support, please check the docs: https://huggingface.co/docs/huggingface_hub/v0.22.2/en/guides/inference
6
- """
7
- client = InferenceClient("HuggingFaceH4/zephyr-7b-beta")
8
-
9
-
10
- def respond(
11
- message,
12
- history: list[tuple[str, str]],
13
- system_message,
14
- max_tokens,
15
- temperature,
16
- top_p,
17
- ):
18
- messages = [{"role": "system", "content": system_message}]
19
-
20
- for val in history:
21
- if val[0]:
22
- messages.append({"role": "user", "content": val[0]})
23
- if val[1]:
24
- messages.append({"role": "assistant", "content": val[1]})
25
-
26
- messages.append({"role": "user", "content": message})
27
-
28
- response = ""
29
-
30
- for message in client.chat_completion(
31
- messages,
32
- max_tokens=max_tokens,
33
- stream=True,
34
- temperature=temperature,
35
- top_p=top_p,
36
- ):
37
- token = message.choices[0].delta.content
38
-
39
- response += token
40
- yield response
41
-
42
-
43
- """
44
- For information on how to customize the ChatInterface, peruse the gradio docs: https://www.gradio.app/docs/chatinterface
45
- """
46
- demo = gr.ChatInterface(
47
- respond,
48
- additional_inputs=[
49
- gr.Textbox(value="You are a friendly Chatbot.", label="System message"),
50
- gr.Slider(minimum=1, maximum=2048, value=512, step=1, label="Max new tokens"),
51
- gr.Slider(minimum=0.1, maximum=4.0, value=0.7, step=0.1, label="Temperature"),
52
- gr.Slider(
53
- minimum=0.1,
54
- maximum=1.0,
55
- value=0.95,
56
- step=0.05,
57
- label="Top-p (nucleus sampling)",
58
- ),
59
- ],
60
- )
61
-
62
-
63
- if __name__ == "__main__":
64
- demo.launch()
 
1
+ from main import *
2
+
3
+ if __name__ == "__main__":
4
+ # Optionally: sync database from CSVs on startup
5
+ # csvs_to_sqlite() # Uncomment if you want to auto-sync DB
6
+ app = create_app(chat, clear_chat)
7
+ app.launch(debug=settings.DEBUG)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
assets/__init__.py ADDED
@@ -0,0 +1 @@
 
 
1
+ # This file marks the 'assets' directory as a Python package.
assets/knowledge_base/landscape_cafe/landscape_cafe_contact.md ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 📞 Landscape CAFE And Eatery – Contact & Visit Info
2
+
3
+ Planning a trip to Landscape CAFE And Eatery? Here’s everything you need to know to make your visit smooth and delightful!
4
+
5
+ ---
6
+
7
+ ## 🗺️ Address & Map
8
+ - **Address:** Ban Bueng District, Chon Buri 20170, Thailand
9
+ - **Landmark:** Located near the main road, look for the lush green entrance with a pond and garden zone
10
+
11
+ ---
12
+
13
+ ## 🕒 Opening Hours
14
+ - **Open daily:** 09:00 AM – 6:00 PM
15
+ (Occasionally closed for private events or special holidays – check Facebook for updates!)
16
+
17
+ ---
18
+
19
+ ## 🚗 Parking & Access
20
+ - **Parking:** Spacious on-site parking for cars and motorcycles
21
+ - **Public Transport:** Accessible by local taxi or ride-sharing apps
22
+
23
+ ---
24
+
25
+ ## 💬 Contact Channels
26
+ - **Facebook:** [Landscape.cafe.eatery](https://www.facebook.com/Landscape.cafe.eatery/)
27
+ - **TikTok:** Over 500 clips of cafe life, bread, and pond views ([tiktok.com/place/Landscape-CAFE-and-Eatery-21568226297913451](https://www.tiktok.com/place/Landscape-CAFE-and-Eatery-21568226297913451))
28
+ - **Phone:** (Contact number not publicly listed; reach via Facebook message for reservations or questions)
29
+ - **Line:** (Check Facebook page for QR code)
30
+
31
+ ---
32
+
33
+ ## 🦆🐟 Special Note
34
+ Landscape CAFE features friendly ducks and colorful fish in its garden pond, making every visit extra memorable for kids and animal lovers!
35
+
36
+ ---
37
+
38
+ ## ✨ Reservation & Event Info
39
+ - Walk-ins welcome
40
+ - Table reservations recommended on weekends or holidays (message Facebook page for fast response)
41
+ - Pet-friendly (small dogs allowed in outdoor area)
42
+
43
+ ---
44
+
45
+ ## 🌐 Stay Connected
46
+ Follow us on Facebook and TikTok for the latest menu updates, live events, and special offers.
47
+ Share your experience with #LandscapeCafeChonburi!
48
+
49
+ ---
assets/knowledge_base/landscape_cafe/landscape_cafe_menu.md ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🍽️ Landscape CAFE And Eatery – Menu & Signature Dishes
2
+
3
+ Landscape CAFE offers a diverse and mouthwatering menu, blending classic cafe staples with creative signature items you won't find elsewhere in Chonburi!
4
+
5
+ ---
6
+
7
+ ## ☕ Drinks & Coffee
8
+ - Premium espresso, Americano, cappuccino, latte, and cold brew
9
+ - Special signature drinks: Iced Butterfly Pea Latte, Rose Milk Tea, and Thai Iced Coffee
10
+ - Refreshing smoothies, herbal teas, and sparkling sodas
11
+
12
+ ---
13
+
14
+ ## 🍰 Baked Goods & Desserts
15
+ - House-made warm bread, served fresh every day
16
+ - Classic croissants, chocolate rolls, and assorted pastries
17
+ - Signature cakes: Lemon Cheesecake, Dark Chocolate Fudge, Matcha Roll
18
+
19
+ ---
20
+
21
+ ## 🦆🐟 Duck & Fish Specials
22
+ Landscape CAFE isn't just about drinks—there are also unique menu items inspired by the resident animals!
23
+ - **Crispy Duck Confit**: Tender duck leg, slow-cooked and crisped to perfection, served with tangy orange sauce
24
+ - **Spicy Grilled Fish Fillet**: Fresh fish from the cafe pond, marinated in local herbs, grilled over charcoal, and served with Thai-style dipping sauce
25
+
26
+ ---
27
+
28
+ ## 🍝 Savory & Comfort Dishes
29
+ - Creamy Carbonara, Spaghetti Tom Yum, Japanese Curry Rice
30
+ - Caesar Salad with grilled chicken or smoked salmon
31
+ - All-day breakfast platters with eggs, sausages, and toast
32
+
33
+ ---
34
+
35
+ ## 🌟 Recommended For First Timers
36
+ 1. **Warm Bread Basket** – Served with signature spreads and homemade butter
37
+ 2. **Butterfly Pea Latte** – Instagrammable and delicious
38
+ 3. **Crispy Duck Confit** – Unmissable for duck lovers!
39
+ 4. **Homemade Lemon Cheesecake** – Tangy, rich, and the perfect pairing for coffee
40
+
41
+ ---
42
+
43
+ ## 📝 Note
44
+ The menu changes seasonally and occasionally features limited-time specials.
45
+ Vegan and gluten-free options are also available on request.
46
+ Don’t forget to check out the daily pastry counter for freshly baked surprises!
47
+
48
+ ---
49
+
assets/knowledge_base/landscape_cafe/landscape_cafe_overview.md ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🌳 Landscape CAFE And Eatery – Overview
2
+
3
+ Welcome to **Landscape CAFE And Eatery**, the iconic destination for coffee lovers and foodies in Chonburi!
4
+ Nestled in Ban Bueng district, this standalone cafe is famous for its lush garden vibe, playful pets (yes, ducks and fish included 🦆🐟), and relaxing outdoor seating.
5
+
6
+ ---
7
+
8
+ ## 📍 Location & Branch Info
9
+ - **Address:** Ban Bueng District, Chon Buri 20170, Thailand
10
+ - **Branch:** Only one original branch (no franchises or chains – authenticity guaranteed!)
11
+ - **Landmark:** Easily spotted by the surrounding green space and cheerful signage.
12
+
13
+ ---
14
+
15
+ ## 🌿 Ambience
16
+ Step into a green paradise! Landscape CAFE is surrounded by beautiful trees, garden walkways, and nature-inspired corners perfect for relaxation or Instagram.
17
+ The outdoor zone is spacious, with cozy tables, comfortable benches, and a small pond filled with playful fish and the resident ducks, bringing an extra dose of joy to your visit.
18
+
19
+ ---
20
+
21
+ ## ✨ Key Highlights
22
+ - Unique pet-friendly concept: ducks and fish are part of the cafe's charm.
23
+ - Well-designed zones for solo visitors, couples, and families.
24
+ - Spacious parking lot and clear entrance from the main road.
25
+ - Natural sunlight, soothing background music, and plenty of greenery everywhere.
26
+ - Perfect for those seeking a chill spot to read, work, or catch up with friends.
27
+
28
+ ---
29
+
30
+ ## 📝 Summary
31
+ Landscape CAFE And Eatery stands out not only for its menu but also for its tranquil setting and attention to detail in every corner.
32
+ Whether you're in Chonburi for a weekend or a local looking for a new favorite hangout, this cafe guarantees a memorable, nature-filled experience with a friendly vibe.
33
+ You might even make a new feathery or finned friend! 🦆🐟
34
+
35
+ ---
assets/knowledge_base/landscape_cafe/landscape_cafe_reviews_and_tips.md ADDED
@@ -0,0 +1,50 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🌟 Landscape CAFE And Eatery – Reviews, Tips & Fun Facts
2
+
3
+ Landscape CAFE And Eatery has quickly become one of Chonburi’s top destinations for both locals and travelers seeking great food, fresh air, and a playful garden experience.
4
+
5
+ ---
6
+
7
+ ## 📝 Customer Reviews
8
+
9
+ > “The atmosphere is so relaxing! Loved sitting by the pond watching ducks and fish while enjoying my coffee.”
10
+ > — Mint, Bangkok
11
+
12
+ > “Best warm bread in Chonburi! Highly recommend the butterfly pea latte and lemon cheesecake.”
13
+ > — Peem, Chonburi
14
+
15
+ > “Family-friendly, plenty of parking, and the staff were super attentive. Our kids loved feeding the fish.”
16
+ > — Kwan, Pattaya
17
+
18
+ ---
19
+
20
+ ## 💡 Visitor Tips
21
+
22
+ - **Arrive early** on weekends for the best garden seats and fresh pastries.
23
+ - **Don’t forget your camera**: The ducks, pond, and lush greenery make for perfect photo ops!
24
+ - **Check Facebook for special closure announcements** to avoid disappointment.
25
+ - **Feeding the fish and ducks** is allowed—ask staff for fish food or bread scraps!
26
+ - **Try something new:** Seasonal specials are often surprising and delicious.
27
+
28
+ ---
29
+
30
+ ## ⚠️ Things to Note
31
+
32
+ - Outdoor seating may be limited during rainy days.
33
+ - Menu may change without notice—some signature items sell out fast.
34
+ - Phone contact is mostly via social channels; immediate walk-in is always an option.
35
+
36
+ ---
37
+
38
+ ## 🎉 Fun Facts
39
+
40
+ - Landscape CAFE’s resident ducks have their own fan club among regulars!
41
+ - Some desserts are inspired by what’s growing in the garden at the time.
42
+ - The pond is home to over 30 colorful fish—spotting the rare golden carp is said to bring good luck!
43
+
44
+ ---
45
+
46
+ ## 🦆🐟 Why Visit?
47
+ Come for the coffee, stay for the garden, and leave with a smile (and maybe a selfie with a duck or fish)!
48
+ Landscape CAFE And Eatery isn’t just a cafe—it’s a little oasis in Chonburi.
49
+
50
+ ---
assets/knowledge_base/promotion/landscape_cafe_promos_august.md ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🎊 August Promotions – Landscape CAFE And Eatery
2
+
3
+ August is all about good vibes, back-to-school fun, and feeding as many beaks and fins as you can! Check out this month’s tongue-in-cheek deals:
4
+
5
+ ---
6
+
7
+ ## 1. 📚 **Back to School, Back to Cool**
8
+ Show your student ID and get **15% off** all iced coffees.
9
+ *Teachers get an extra high-five (and zero discount, sorry!)*
10
+
11
+ ---
12
+
13
+ ## 2. 🦆 **Duck Dynasty Package**
14
+ Order any breakfast set and receive **premium duck food** for free—upgrade your morning and a duck’s, too.
15
+ *Quacking not included.*
16
+
17
+ ---
18
+
19
+ ## 3. 🐟 **Ultimate Pond Party**
20
+ Buy any two main dishes, get a complimentary “pond party pack”: includes both fish food and duck treats.
21
+ *Perfect for family outings or aspiring animal influencers.*
22
+
23
+ ---
24
+
25
+ ## 4. ☕ **Espresso Yourself!**
26
+ Every 10th espresso served each day is on the house—
27
+ *Just hope you’re lucky #10 (or drink a lot of espresso and improve your odds).*
28
+
29
+ ---
30
+
31
+ ## 5. 🍰 **Cake for the Brave**
32
+ Order our Spicy Grilled Fish Fillet and get **20% off** any cake slice.
33
+ *Reward yourself for your adventurous taste buds (and maybe cool your mouth down).*
34
+
35
+ ---
36
+
37
+ ## 💡 Ongoing Special
38
+ **Buy any fish food, get a free pack of duck snacks**—again!
39
+ Because sharing is caring, and you deserve to make every animal happy.
40
+
41
+ ---
assets/knowledge_base/promotion/landscape_cafe_promos_july.md ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # 🎉 July Promotions – Landscape CAFE And Eatery
2
+
3
+ Welcome to July! It’s hot, it’s chill, and so are our deals. Grab your favorite coffee, snap a selfie with a duck, and don’t forget to read the fine (and funny) print!
4
+
5
+ ---
6
+
7
+ ## 1. 🦆 **Duck Buddy Combo**
8
+ Buy any large coffee, get a **free pack of duck snacks** to feed our café ducks.
9
+ *Share your treats, gain a new feathery friend—just don’t let them steal your cake!*
10
+
11
+ ---
12
+
13
+ ## 2. ☀️ **Heatwave Relief Set**
14
+ Order any cold drink + pastry and get **10% off** on your second cold drink (because Chonburi summer is relentless).
15
+ *Proof of sweat not required.*
16
+
17
+ ---
18
+
19
+ ## 3. 🍞 **Bread Addiction Card**
20
+ Buy fresh warm bread 3 times in July, get the 4th **on the house**!
21
+ *Bread club membership comes with invisible status.*
22
+
23
+ ---
24
+
25
+ ## 4. 🐟 **Fishy Business Bonus**
26
+ Purchase any savory meal and get the option to buy fish food at **50% off**.
27
+ *Feed the pond fish—they’re hungry, dramatic, and always grateful.*
28
+
29
+ ---
30
+
31
+ ## 5. 🛵 **Lazy Delivery Flash Sale**
32
+ Order takeaway via phone or Facebook, tell us you’re “too lazy to leave the house,” and get a **random mystery discount**.
33
+ *Discount value depends on staff mood.*
34
+
35
+ ---
36
+
37
+ ## 💡 Ongoing Special
38
+ **Buy fish food, get free duck snacks.**
39
+ Why choose? Make both the ducks and fish your best friends!
40
+
41
+ ---
assets/raw_data/customer.csv ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ customer_id,first_name,last_name,email,phone,registration_date,member_level,notes
2
+ C001,Nina,Quirk,[email protected],081-123-4567,2023-09-15,Gold,"Loves bread, ducks, and special drinks"
3
+ C002,Pat,Prank,[email protected],082-222-9876,2024-02-11,Silver,"Always asks about secret menu"
4
+ C003,Ray,Rebel,[email protected],083-765-4321,2024-11-30,Bronze,"Prefers outdoor seating, loves coffee"
5
+ C004,Jen,Gizmo,[email protected],086-666-1111,2025-01-20,Gold,"Brings family on weekends"
6
+ C005,Lynn,Loop,[email protected],084-808-8080,2023-12-07,Silver,"Feeds ducks every visit"
7
+ C006,Poom,Owner,[email protected],089-999-0000,2023-07-01,Owner,"The owner – always on site, loves ducks, knows every customer"
assets/raw_data/menu_list.csv ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ product_id,product_name,category,price,description,is_active
2
+ P001,Americano,Drink,60,"Classic hot or iced black coffee",1
3
+ P002,Butterfly Pea Latte,Drink,80,"Signature blue latte with floral notes",1
4
+ P003,Iced Thai Tea,Drink,65,"Sweet and creamy, traditional Thai tea",1
5
+ P004,Matcha Latte,Drink,75,"Japanese green tea latte, rich and smooth",1
6
+ P005,Sparkling Lemon Soda,Drink,70,"Homemade lemon soda with bubbles",1
7
+ P006,Spicy Grilled Fish Fillet,Food,150,"Charcoal-grilled local fish with Thai herbs and spicy dip",1
8
+ P007,Crispy Duck Confit,Food,190,"Slow-cooked duck leg, crispy skin, served with orange sauce",1
9
+ P008,Carbonara Pasta,Food,120,"Creamy pasta with bacon and parmesan",1
10
+ P009,Caesar Salad,Food,95,"Fresh romaine, croutons, grilled chicken, Caesar dressing",1
11
+ P010,Tom Yum Spaghetti,Food,130,"Fusion of Italian spaghetti and Thai tom yum flavors",1
12
+ P011,Warm Bread Basket,Bakery,90,"Freshly baked bread served with homemade butter",1
13
+ P012,Lemon Cheesecake,Bakery,95,"Tangy, creamy cheesecake with lemon zest",1
14
+ P013,Matcha Roll,Bakery,85,"Soft roll cake with matcha cream filling",1
15
+ P014,Chocolate Croissant,Bakery,55,"Flaky croissant filled with rich chocolate",1
16
+ P015,Classic Brownie,Bakery,60,"Dense chocolate brownie with walnut topping",1
assets/raw_data/order_items.csv ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ order_item_id,order_id,product_id,quantity,unit_price,discount,subtotal
2
+ OI001,O1001,P001,1,60,0,60
3
+ OI002,O1001,P011,1,90,0,90
4
+ OI003,O1001,P014,1,55,0,55
5
+ OI004,O1001,P003,1,65,0,65
6
+ OI005,O1002,P002,1,80,10,72
7
+ OI006,O1002,P012,1,95,0,95
8
+ OI007,O1003,P011,2,90,0,180
9
+ OI008,O1003,P007,1,190,20,152
10
+ OI009,O1003,P013,1,85,0,85
11
+ OI010,O1004,P006,1,150,0,150
12
+ OI011,O1005,P003,1,65,0,65
13
+ OI012,O1005,P015,1,60,0,60
14
+ OI013,O1006,P007,1,190,0,190
15
+ OI014,O1006,P009,1,95,0,95
16
+ OI015,O1006,P004,1,75,0,75
assets/raw_data/orders.csv ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ order_id,customer_id,order_datetime,total_amount,order_status,notes
2
+ O1001,C001,2025-07-18 09:30,230,Completed,"Had breakfast with coffee and bakery"
3
+ O1002,C002,2025-07-18 11:45,185,Completed,"Lunch with signature latte and dessert"
4
+ O1003,C004,2025-07-19 10:12,335,Completed,"Family visit, bakery & main dishes"
5
+ O1004,C003,2025-07-20 16:30,150,Completed,"Late lunch with fish dish and coffee"
6
+ O1005,C005,2025-07-21 14:55,85,Completed,"Only drinks and bakery"
7
+ O1006,C006,2025-07-21 17:05,250,Completed,"Owner's dinner meeting"
assets/raw_data/promotions.csv ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ promo_id,promo_name,start_date,end_date,description,is_active
2
+ PR01,Duck Buddy Combo,2025-07-01,2025-07-31,"Buy large coffee, get free duck snack",1
3
+ PR02,Heatwave Relief Set,2025-07-01,2025-07-31,"Cold drink + pastry 10% off 2nd drink",1
4
+ PR03,Bread Addiction Card,2025-07-01,2025-07-31,"Buy bread 3 times get 4th free",1
5
+ PR04,Fishy Business Bonus,2025-07-01,2025-07-31,"Savory meal unlocks 50% off fish food",1
6
+ PR05,Back to School Cool,2025-08-01,2025-08-31,"Student ID gets 15% off iced coffees",0
assets/raw_data/reservations.csv ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ reservation_id,customer_id,table_id,reservation_datetime,status,notes
2
+ R1001,C004,T03,2025-07-19 09:30,Confirmed,"Family reservation, 6 seats"
3
+ R1002,C002,T02,2025-07-18 11:30,Completed,"Lunch, preferred near window"
4
+ R1003,C006,T05,2025-07-21 16:30,Confirmed,"Owner's group, big table"
assets/raw_data/staff.csv ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ staff_id,name,role,phone,hire_date,status
2
+ S001,May,Barista,080-555-1234,2023-08-01,Active
3
+ S002,Bob,Chef,089-666-2222,2022-10-15,Active
4
+ S003,Gift,Cashier,085-777-3333,2024-05-20,Active
5
+ S004,Poom,Owner,089-999-0000,2023-07-01,Active
6
+ S005,June,Waitress,081-444-5678,2024-03-10,Active
assets/raw_data/table_status.csv ADDED
@@ -0,0 +1,6 @@
 
 
 
 
 
 
 
1
+ table_id,table_number,seats,location,status
2
+ T01,1,4,Indoor,Available
3
+ T02,2,2,Indoor,Occupied
4
+ T03,3,6,Outdoor,Reserved
5
+ T04,4,2,Outdoor,Available
6
+ T05,5,8,Outdoor,Available
config/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ # This file marks the 'config' directory as a Python package.
2
+
3
+
4
+
config/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (183 Bytes). View file
 
config/__pycache__/agent_registry.cpython-312.pyc ADDED
Binary file (549 Bytes). View file
 
config/__pycache__/config.cpython-312.pyc ADDED
Binary file (6.73 kB). View file
 
config/__pycache__/settings.cpython-312.pyc ADDED
Binary file (1.02 kB). View file
 
config/__pycache__/ui_config.cpython-312.pyc ADDED
Binary file (1.06 kB). View file
 
config/agent_registry.py ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # agent_registry.py
2
+
3
+ from agents.intake_agent import IntakeAgent
4
+ from agents.cafe_bot import LanscapeCafeBot
5
+ from agents.database_agent import CoffeeDatabaseAgent
6
+ from agents.aggregator_agent import AggregatorAgent
7
+
8
+ AGENT_REGISTRY = {
9
+ "intake_agent": IntakeAgent,
10
+ "landscape_cafe_bot": LanscapeCafeBot,
11
+ "coffee_db_agent": CoffeeDatabaseAgent,
12
+ "aggregator_agent": AggregatorAgent,
13
+ }
config/config.py ADDED
@@ -0,0 +1,100 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ALLOWED_AGENTS = {
2
+ "landscape_cafe_bot": {
3
+ "display_name": "Landscape Cafe Bot",
4
+ "capability": (
5
+ "Provides comprehensive information and support for all products, menus, promotions detail, owner profile, and general cafe services. "
6
+ "Can answer questions about drinks, food, cafe facilities, opening hours, current promotions, and general customer inquiries. "
7
+ "Utilizes the RAG system to retrieve up-to-date cafe knowledge and answer user questions accurately."
8
+ ),
9
+ "example_command": (
10
+ "As of {Date_time}\n"
11
+ "Please describe the difference between the Matcha Latte and the Espresso Tonic, and let the customer know about any ongoing promotions for iced drinks today. "
12
+ "If there is a special event or seasonal menu, please include those details as well."
13
+ ),
14
+ },
15
+ "coffee_db_agent": {
16
+ "display_name": "Coffee Database Agent",
17
+ "capability": (
18
+ "Manages and accesses all structured business data related to the cafe. "
19
+ "Can retrieve, update, and summarize details on products, menu prices, inventory, member and customer profiles, order and sales activity, promotions, reservations, and staff. "
20
+ "Supports operations such as checking current stock, tracking daily or historical sales, viewing or updating customer and membership info, listing active promotions, and monitoring booking and order statuses. "
21
+ "Enables comprehensive reporting, real-time lookups, and targeted updates to support all cafe operations, without requiring knowledge of technical database structure or table names."
22
+ ),
23
+ "example_command": (
24
+ "As of {Date_time}\n"
25
+ "Please retrieve the latest bill details for customer ID #98765, check the delivery status of order #12345, and provide the current membership points balance."
26
+ ),
27
+ },
28
+ "END": {
29
+ "display_name": "End/No Action",
30
+ "capability": "Marks the end of workflow; no further processing required.",
31
+ "example_command": ""
32
+ }
33
+ }
34
+
35
+ RESPONSIBLITY = (
36
+ "As the Intake Officer for Landscape Cafe & Eatery, your mission is to ensure every customer receives clear, friendly, and accurate assistance. "
37
+ "Route every question—whether about our food menu, drink selection, special events, café facilities, orders, or customer service—"
38
+ "to the most suitable expert agent. Your tone should always be welcoming and informative, reflecting the warm and cozy spirit of our cafe."
39
+ )
40
+
41
+ INTAKE_PROMPT = """You are the Intake Officer for a customer support AI system for this company.
42
+ Your ONLY job is to greet, clarify, and triage user requests by gathering all necessary details, then routing the request to the most appropriate agent. You never handle or resolve requests yourself—your mission is to be the friendly, witty “front desk” that makes sure the next agent always has everything they need.
43
+
44
+ **Your Primary Responsibility:**
45
+ {responsibility}
46
+
47
+ **Agent Capabilities:**
48
+ {get_agent_capabilities}
49
+
50
+ **Today's Date and Time:** {Date_time}
51
+
52
+ **Instructions:**
53
+ - Only assign tasks to agents listed above. Select at least one agent for each user request, if relevant.
54
+ - Never reply to or resolve user requests directly (unless explicitly instructed as “END” below).
55
+ - Your job is to clarify the user’s request, ask for any missing or unclear details (with friendly, engaging, or humorous follow-up questions and emojis!), then forward all clear and complete information to the right agent(s).
56
+ - If the user’s request is incomplete or unclear, reply as "END" and ask friendly, funny, or specific follow-up questions (with emojis) to gather more details. Set "finish": true.
57
+ - If you are completely sure the request can’t be handled by any agent, reply as "END" with a polite, lighthearted, emoji-filled message explaining this. Assign a single "END" assignment with your reply and set "finish": true.
58
+ - Do not generate solutions, data, or final answers for the user, and do not perform actions outside of your defined role.
59
+ - If the request is clear, for each relevant agent, generate a clear, step-by-step command that includes all required details from the user’s message. Make your instruction so complete that the agent can act independently.
60
+ - If any details are still missing but the intent is clear, specify in your command what extra information the agent should request from the user.
61
+ - Always mention today’s date/time if it’s relevant.
62
+ - Always reply and write commands in the same language as the user's message (Thai, English, or else).
63
+ - Do not process or assign requests that are not appropriate or not supported by the available agents.
64
+
65
+ **Output Format:**
66
+ Return a JSON object with an `assignments` key.
67
+ Each assignment object includes:
68
+ - "agent": (str) The agent's name; must be in: {agent_names}
69
+ - "command": (str) Your instruction for the agent, or your reply to the user (always in the user's language, friendly, and with emojis if possible!)
70
+ - "result": (None) Leave blank for agent response.
71
+ - "finish": (bool) Set to true only if replying directly to the user or ending the workflow; otherwise, false.
72
+ - If you assign "END", always set "finish": true to indicate workflow completion.
73
+
74
+ **Example:**
75
+ If a request requires multiple agents, reply and write all agent commands in the exact same language as the user’s message:
76
+ ```json
77
+ {{
78
+ "assignments": [
79
+ {{
80
+ "agent": (str) The agent's name; choose only from: {agent_names},
81
+ "command": "As of {Date_time}, please check the current status of the user's order for the SuperWidget 3000 and provide detailed updates including tracking info. If the order number is missing, ask the user for it." or "ช่วยตรวจสอบรายละเอียดของสินค้า ... พร้อมรายละเอียดโปรโมชั่น ณ วันที่ {Date_time} ให้ลูกค้าทราบด้วยครับ 😊",
82
+ "finish": false
83
+ }},
84
+ {{
85
+ "agent": (str) The agent's name; choose only from: {agent_names},
86
+ "command": "As of {Date_time}, please retrieve the latest billing receipt, including purchase date, total, and payment method. If the user's full name or account ID is missing, please request it from the user.",
87
+ "finish": false
88
+ }}
89
+ ]
90
+ }}
91
+ """
92
+
93
+ PRODUCT_AGENT_PROMPT = (
94
+ "You are a helpful product support AI.\n"
95
+ "- Answer user questions only using the reference context provided.\n"
96
+ "- If the answer is not in the context, tell user that you do not have enough information.\n"
97
+ "- Respond with clear and polite language. You may use emojis to enhance your response.\n"
98
+ "- Do not include opinions, further explanations, or follow-up questions in your replies.\n"
99
+ "- Always use the same language as the user."
100
+ )
config/settings.py ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from pathlib import Path
3
+
4
+ # Debug mode
5
+ DEBUG = True
6
+
7
+ # Base directory
8
+ BASE_DIR = Path(__file__).parent.parent.resolve()
9
+
10
+ # Path settings
11
+ ASSETS_DIR = BASE_DIR / "assets"
12
+ DATABASE_DIR = BASE_DIR / "database"
13
+ RAG_DIR = BASE_DIR / "rag"
14
+ UI_DIR = BASE_DIR / "ui"
15
+
16
+ # Database
17
+ DATABASE_PATH = DATABASE_DIR / "database.db"
18
+
19
+ # RAG/Embedding
20
+ KNOWLEDGE_BASE_PATH = ASSETS_DIR / "knowledge_base"
21
+ EMBEDDING_PATH = RAG_DIR / "embedding"
22
+ EMBEDDING_MODEL_NAME = "BAAI/bge-m3"
23
+ COLLECTION_NAME = "landscape_cafe"
24
+
25
+ # Gradio UI
26
+ APP_TITLE = "Landscape Cafe & Eatery Chatbot"
27
+
28
+ # API/LLM
29
+ GOOGLE_API_KEY = os.environ.get("GOOGLE_API_KEY", "")
30
+ GEMINI_MODEL = "gemini-2.5-flash-preview-05-20"
31
+
32
+ # Other settings can be added here
config/ui_config.py ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # ui_config.py
2
+ from config.settings import UI_DIR
3
+
4
+ # UI Theme
5
+ THEME = "Soft"
6
+
7
+ # Logo and images
8
+ LOGO_PATH = UI_DIR / "images/icon_app.png"
9
+ COVER_PATH = UI_DIR / "images/landscape_cover.jpg"
10
+ ICON_PATH = UI_DIR / "images/landscape_icon.jpg"
11
+
12
+ # UI Texts
13
+ APP_TITLE = "Landscape Cafe & Eatery Chatbot"
14
+ WELCOME_TITLE = "☕ Landscape Cafe & Eatery Assistant 🌿"
15
+ WELCOME_MESSAGE = "Welcome to our cozy cafe! I'm here to help you with menu, promotions, and reservations."
16
+
17
+ # Example questions
18
+ EXAMPLES = [
19
+ "Location and parking lots?",
20
+ "เวลาเปิดปิดร้าน",
21
+ "โปรโมชั่นประจำเดือน",
22
+ "ขอรายการสินค้าที่มีพร้อมราคา",
23
+ "Sales this month",
24
+ "ตรวจสอบโต๊ะว่างขณะนี้และช่องทางติดต่อ"
25
+ ]
database/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ # This file marks the 'database' directory as a Python package.
2
+
3
+
4
+
database/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (177 Bytes). View file
 
database/__pycache__/create_db.cpython-312.pyc ADDED
Binary file (2.76 kB). View file
 
database/create_db.py ADDED
@@ -0,0 +1,48 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import glob
3
+ import pandas as pd
4
+ import sqlite3
5
+ from config import settings
6
+
7
+ def csvs_to_sqlite(csv_folder: str = None, db_path: str = None):
8
+ """
9
+ Convert all CSV files in csv_folder to tables in SQLite DB at db_path.
10
+ - If a CSV is updated, update table.
11
+ - If a CSV is deleted, drop table in DB.
12
+ - If a new CSV is added, create new table.
13
+ Paths are configurable via config.settings.
14
+ """
15
+ # Use config if not provided
16
+ csv_folder = str(csv_folder or (settings.ASSETS_DIR / "raw_data"))
17
+ db_path = str(db_path or settings.DATABASE_PATH)
18
+
19
+ # 1. Scan all csv files in the folder
20
+ csv_files = {os.path.splitext(os.path.basename(f))[0]: f
21
+ for f in glob.glob(os.path.join(csv_folder, "*.csv"))}
22
+ # 2. Connect to the SQLite database
23
+ conn = sqlite3.connect(db_path)
24
+ cursor = conn.cursor()
25
+
26
+ # 3. Get existing tables in the database
27
+ cursor.execute("SELECT name FROM sqlite_master WHERE type='table';")
28
+ existing_tables = set(row[0] for row in cursor.fetchall())
29
+
30
+ # 4. Create/update tables from CSV files
31
+ for table, file in csv_files.items():
32
+ df = pd.read_csv(file)
33
+ # Overwrite table (if exists) with current CSV
34
+ df.to_sql(table, conn, if_exists='replace', index=False)
35
+ print(f"[DB SYNC] Table '{table}' updated from {file}")
36
+
37
+ # 5. Drop tables that have no corresponding CSV anymore
38
+ for table in existing_tables:
39
+ if table not in csv_files:
40
+ cursor.execute(f"DROP TABLE IF EXISTS {table};")
41
+ print(f"[DB SYNC] Table '{table}' dropped (no CSV found)")
42
+
43
+ conn.commit()
44
+ conn.close()
45
+ print("[DB SYNC] Database sync completed.")
46
+
47
+ if __name__ == "__main__":
48
+ csvs_to_sqlite()
database/database.db ADDED
Binary file (36.9 kB). View file
 
main.py ADDED
@@ -0,0 +1,103 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from config import settings
3
+ from config.agent_registry import AGENT_REGISTRY
4
+ from rag.rag_system import RAGSystem
5
+ from workflows.state import AssignmentResponse
6
+ from workflows.graph import build_workflow
7
+ from ui.app import create_app
8
+ import gradio as gr
9
+
10
+ # Import LLM (Gemini) and other dependencies as needed
11
+ from langchain_google_genai import ChatGoogleGenerativeAI
12
+
13
+ # --- Initialize RAGSystem ONCE (global, not per chat) ---
14
+ rag = RAGSystem() # Uses config.settings by default
15
+
16
+ # --- Factory functions for dependencies ---
17
+
18
+ def get_llm(api_key: str):
19
+ """Create a Gemini LLM instance with the given API key."""
20
+ return ChatGoogleGenerativeAI(model=settings.GEMINI_MODEL, google_api_key=api_key)
21
+
22
+ # --- Agent node wrappers for workflow ---
23
+
24
+ def make_agents(api_key: str, rag: RAGSystem):
25
+ """
26
+ Create agent node functions for the workflow, injecting dependencies.
27
+ Returns a dict of node_name: callable(state) -> dict
28
+ """
29
+ llm = get_llm(api_key)
30
+ # Intake agent expects a Gemini LLM with structured output
31
+ intake_llm = llm.with_structured_output(AssignmentResponse)
32
+ agents = {
33
+ "intake_agent": AGENT_REGISTRY["intake_agent"](gemini_with_output=intake_llm),
34
+ "landscape_cafe_bot": AGENT_REGISTRY["landscape_cafe_bot"](rag_system=rag, gemini_agent=llm),
35
+ "coffee_db_agent": AGENT_REGISTRY["coffee_db_agent"](db_path=settings.DATABASE_PATH, llm_agent=llm),
36
+ "aggregator_agent": AGENT_REGISTRY["aggregator_agent"](gemini_agent=llm),
37
+ }
38
+ # Wrap each agent as a node function for the workflow
39
+ def node_wrapper(agent):
40
+ def node_fn(state):
41
+ return agent.process(state)
42
+ return node_fn
43
+ return {k: node_wrapper(v) for k, v in agents.items()}
44
+
45
+ # --- Workflow state and logic ---
46
+
47
+ def build_supportflowx_workflow(api_key: str, rag: RAGSystem):
48
+ """
49
+ Build the workflow graph with all agent nodes and dependencies.
50
+ """
51
+ agent_nodes = make_agents(api_key, rag)
52
+ workflow = build_workflow(
53
+ agent_nodes["intake_agent"],
54
+ agent_nodes["landscape_cafe_bot"],
55
+ agent_nodes["coffee_db_agent"],
56
+ agent_nodes["aggregator_agent"]
57
+ )
58
+ return workflow
59
+
60
+ # --- Gradio Chat/Backend Logic ---
61
+
62
+ def chat(user_message: str, history: list = None, api_key: str = None):
63
+ """
64
+ Handle a chat message from the UI. Returns updated history and logs.
65
+ """
66
+ try:
67
+ if not api_key or not api_key.startswith("AI"):
68
+ return history or [], "⚠️ Please enter a valid Gemini API Key."
69
+ if not user_message.strip():
70
+ return history or [], "⚠️ Please fill in the message."
71
+ # Build workflow for this session (could be cached per api_key)
72
+ workflow = build_supportflowx_workflow(api_key, rag)
73
+ allowed_agents = list(AGENT_REGISTRY.keys())
74
+ chat_history = history.copy() if history else []
75
+ state = {
76
+ "user_message": user_message,
77
+ "chat_history": chat_history,
78
+ "allowed_agents": allowed_agents,
79
+ "assigned_agents": {"assignments": []},
80
+ "final_response": "",
81
+ "logs": []
82
+ }
83
+ result = workflow.invoke(state)
84
+ bot_reply = (result.get("final_response") or "Sorry, I did not understand your question. Please try again.")
85
+ logs = "\n".join(result.get("logs", []))
86
+ chat_history.append({"role": "user", "content": user_message})
87
+ chat_history.append({"role": "assistant", "content": bot_reply})
88
+ # Limit history to last 20 messages
89
+ chat_history = chat_history[-20:]
90
+ return chat_history, logs
91
+ except Exception as e:
92
+ return history or [], f"❌ Internal error: {str(e)[:500]}"
93
+
94
+ def clear_chat():
95
+ """Clear the chat and logs."""
96
+ return [], ""
97
+
98
+ # --- Main entry point ---
99
+ if __name__ == "__main__":
100
+ # Optionally: sync database from CSVs on startup
101
+ # csvs_to_sqlite() # Uncomment if you want to auto-sync DB
102
+ app = create_app(chat, clear_chat)
103
+ app.launch(debug=settings.DEBUG)
rag/__init__.py ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ # This file marks the 'rag' directory as a Python package.
2
+
3
+
4
+
rag/__pycache__/__init__.cpython-312.pyc ADDED
Binary file (180 Bytes). View file
 
rag/__pycache__/rag_system.cpython-312.pyc ADDED
Binary file (15 kB). View file