Kiki0203 commited on
Commit
3521f3d
Β·
verified Β·
1 Parent(s): 7f45575

Upload 3 files

Browse files
Files changed (3) hide show
  1. .gitignore +14 -0
  2. app.py +121 -288
  3. requirements.txt +1 -0
.gitignore ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.pyo
4
+ .env
5
+ .env.*
6
+ *.egg-info/
7
+ dist/
8
+ build/
9
+ .venv/
10
+ venv/
11
+ env/
12
+ .DS_Store
13
+ *.log
14
+ flagged/
app.py CHANGED
@@ -7,328 +7,161 @@ from langgraph.graph.message import add_messages
7
  from langchain_groq import ChatGroq
8
  from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
9
 
10
- # ── State ────────────────────────────────────────────────────────────────────
11
-
12
  class State(TypedDict):
13
  messages: Annotated[list, add_messages]
14
 
15
- # ── LLM & Graph ──────────────────────────────────────────────────────────────
16
-
17
- SYSTEM_PROMPT = """You are a brilliant, friendly, and concise general-purpose AI assistant.
18
- You help users with anything they ask β€” questions, writing, analysis, coding, brainstorming, and more.
19
- Be warm, clear, and genuinely helpful. When you don't know something, say so honestly."""
20
-
21
  def get_llm():
22
- """Return the best available Groq model."""
23
- api_key = os.environ.get("GROQ_API_KEY", "")
24
- # Try models in order of preference
25
- for model in [
26
- "llama-3.3-70b-versatile",
27
- "llama-3.1-70b-versatile",
28
- "mixtral-8x7b-32768",
29
- "llama-3.1-8b-instant",
30
- ]:
31
- try:
32
- llm = ChatGroq(model=model, api_key=api_key, temperature=0.7)
33
- return llm, model
34
- except Exception:
35
- continue
36
- raise RuntimeError("No Groq model available. Check your GROQ_API_KEY.")
37
-
38
- llm, active_model = get_llm()
39
 
40
- def chatbot_node(state: State):
41
- """Core LangGraph node: calls the LLM with full message history."""
42
- messages = [SystemMessage(content=SYSTEM_PROMPT)] + state["messages"]
43
- response = llm.invoke(messages)
44
- return {"messages": [response]}
 
 
45
 
 
46
  def build_graph():
47
- builder = StateGraph(State)
48
- builder.add_node("chatbot", chatbot_node)
49
- builder.add_edge(START, "chatbot")
50
- builder.add_edge("chatbot", END)
51
- return builder.compile()
52
 
53
- graph = build_graph()
 
 
54
 
55
- # ── Chat logic ────────────────────────────────────────────────────────────────
 
 
 
 
56
 
 
 
 
57
  def chat(user_message: str, history: list):
58
- """Convert Gradio history β†’ LangChain messages β†’ run graph β†’ return reply."""
59
  if not user_message.strip():
60
- return "", history
61
-
62
- # Build message list from history
63
  lc_messages = []
64
- for human, assistant in history:
65
- lc_messages.append(HumanMessage(content=human))
66
- if assistant:
67
- lc_messages.append(AIMessage(content=assistant))
68
  lc_messages.append(HumanMessage(content=user_message))
69
-
70
- # Run through LangGraph
71
  result = graph.invoke({"messages": lc_messages})
72
  ai_reply = result["messages"][-1].content
73
-
74
- history.append((user_message, ai_reply))
75
- return "", history
 
76
 
77
  def clear_chat():
78
- return [], []
79
 
80
- # ── Gradio UI ─────────────────────────────────────────────────────────────────
 
81
 
 
82
  CSS = """
83
- @import url('https://fonts.googleapis.com/css2?family=Syne:wght@400;600;700;800&family=DM+Sans:ital,wght@0,300;0,400;0,500;1,300&display=swap');
84
-
85
- :root {
86
- --bg-primary: #0a0a0f;
87
- --bg-card: #111118;
88
- --bg-input: #1a1a24;
89
- --border: #2a2a3a;
90
- --accent: #7c6af7;
91
- --accent-soft: #9d91f8;
92
- --accent-glow: rgba(124, 106, 247, 0.15);
93
- --text-primary: #f0eff8;
94
- --text-muted: #7a798a;
95
- --user-bubble: linear-gradient(135deg, #7c6af7, #5b4fd4);
96
- --bot-bubble: #1a1a24;
97
- --success: #4ade80;
98
- --radius: 16px;
99
- }
100
-
101
- * { box-sizing: border-box; }
102
-
103
- body, .gradio-container {
104
- background: var(--bg-primary) !important;
105
- font-family: 'DM Sans', sans-serif !important;
106
- color: var(--text-primary) !important;
107
- }
108
-
109
- .gradio-container {
110
- max-width: 860px !important;
111
- margin: 0 auto !important;
112
- padding: 0 !important;
113
- }
114
-
115
- /* Header */
116
- #header {
117
- text-align: center;
118
- padding: 48px 32px 24px;
119
- position: relative;
120
- }
121
-
122
- #header::before {
123
- content: '';
124
- position: absolute;
125
- top: 0; left: 50%;
126
- transform: translateX(-50%);
127
- width: 300px; height: 200px;
128
- background: radial-gradient(ellipse, rgba(124,106,247,0.18) 0%, transparent 70%);
129
- pointer-events: none;
130
- }
131
-
132
- #app-title {
133
- font-family: 'Syne', sans-serif !important;
134
- font-size: 2.6rem !important;
135
- font-weight: 800 !important;
136
- letter-spacing: -0.03em !important;
137
- background: linear-gradient(135deg, #f0eff8 30%, #9d91f8) !important;
138
- -webkit-background-clip: text !important;
139
- -webkit-text-fill-color: transparent !important;
140
- background-clip: text !important;
141
- margin: 0 0 8px !important;
142
- line-height: 1.1 !important;
143
- }
144
-
145
- #app-subtitle {
146
- color: var(--text-muted) !important;
147
- font-size: 1rem !important;
148
- font-weight: 300 !important;
149
- margin: 0 !important;
150
- }
151
-
152
- /* Model badge */
153
- #model-badge {
154
- display: inline-flex;
155
- align-items: center;
156
- gap: 6px;
157
- background: var(--accent-glow);
158
- border: 1px solid rgba(124,106,247,0.3);
159
- border-radius: 100px;
160
- padding: 4px 14px;
161
- margin-top: 14px;
162
- font-size: 0.78rem;
163
- color: var(--accent-soft);
164
- font-family: 'DM Sans', monospace;
165
- }
166
-
167
- /* Chat area */
168
- #chatbot-box {
169
- background: var(--bg-card) !important;
170
- border: 1px solid var(--border) !important;
171
- border-radius: var(--radius) !important;
172
- margin: 0 24px !important;
173
- overflow: hidden !important;
174
- }
175
-
176
- #chatbot-box .wrap {
177
- background: transparent !important;
178
- padding: 16px !important;
179
- }
180
-
181
- /* Messages */
182
- .message {
183
- border-radius: 14px !important;
184
- padding: 14px 18px !important;
185
- font-size: 0.97rem !important;
186
- line-height: 1.65 !important;
187
- max-width: 82% !important;
188
- margin-bottom: 4px !important;
189
- }
190
-
191
- .message.user {
192
- background: var(--user-bubble) !important;
193
- color: #fff !important;
194
- margin-left: auto !important;
195
- box-shadow: 0 4px 20px rgba(124,106,247,0.3) !important;
196
- }
197
-
198
- .message.bot {
199
- background: var(--bot-bubble) !important;
200
- color: var(--text-primary) !important;
201
- border: 1px solid var(--border) !important;
202
- }
203
-
204
- /* Input row */
205
- #input-row {
206
- display: flex;
207
- gap: 10px;
208
- padding: 16px 24px 12px !important;
209
- align-items: flex-end;
210
- }
211
-
212
- #msg-input textarea {
213
- background: var(--bg-input) !important;
214
- border: 1px solid var(--border) !important;
215
- border-radius: 12px !important;
216
- color: var(--text-primary) !important;
217
- font-family: 'DM Sans', sans-serif !important;
218
- font-size: 0.97rem !important;
219
- padding: 14px 16px !important;
220
- resize: none !important;
221
- transition: border-color 0.2s !important;
222
- }
223
-
224
- #msg-input textarea:focus {
225
- border-color: var(--accent) !important;
226
- outline: none !important;
227
- box-shadow: 0 0 0 3px var(--accent-glow) !important;
228
- }
229
-
230
- #msg-input textarea::placeholder { color: var(--text-muted) !important; }
231
-
232
- #msg-input label { display: none !important; }
233
-
234
- /* Buttons */
235
- #send-btn, #clear-btn {
236
- border-radius: 12px !important;
237
- font-family: 'Syne', sans-serif !important;
238
- font-weight: 600 !important;
239
- font-size: 0.9rem !important;
240
- padding: 0 22px !important;
241
- height: 50px !important;
242
- transition: all 0.2s !important;
243
- cursor: pointer !important;
244
- white-space: nowrap !important;
245
- }
246
-
247
- #send-btn {
248
- background: linear-gradient(135deg, #7c6af7, #5b4fd4) !important;
249
- color: #fff !important;
250
- border: none !important;
251
- box-shadow: 0 4px 16px rgba(124,106,247,0.35) !important;
252
- }
253
-
254
- #send-btn:hover {
255
- transform: translateY(-1px) !important;
256
- box-shadow: 0 6px 22px rgba(124,106,247,0.5) !important;
257
- }
258
-
259
- #clear-btn {
260
- background: transparent !important;
261
- color: var(--text-muted) !important;
262
- border: 1px solid var(--border) !important;
263
- }
264
-
265
- #clear-btn:hover {
266
- border-color: #ff6b6b !important;
267
- color: #ff6b6b !important;
268
- background: rgba(255,107,107,0.07) !important;
269
- }
270
-
271
- /* Footer */
272
- #footer {
273
- text-align: center;
274
- padding: 16px 24px 32px;
275
- color: var(--text-muted);
276
- font-size: 0.78rem;
277
- }
278
-
279
- /* Scrollbar */
280
- ::-webkit-scrollbar { width: 5px; }
281
- ::-webkit-scrollbar-track { background: transparent; }
282
- ::-webkit-scrollbar-thumb { background: var(--border); border-radius: 10px; }
283
  """
284
 
285
- with gr.Blocks(css=CSS, title="AI Assistant", theme=gr.themes.Base()) as demo:
 
 
 
 
 
 
 
286
 
287
- # ── Header ──────────────────────────────────────────────────────────────
288
- with gr.Column(elem_id="header"):
289
- gr.HTML(f"""
290
- <h1 id="app-title">AI Assistant</h1>
291
- <p id="app-subtitle">Powered by LangGraph &amp; Groq β€” fast, smart, always on.</p>
292
- <div id="model-badge">
293
- ⚑ {active_model}
294
- </div>
295
- """)
 
 
 
 
 
 
296
 
297
- # ── Chat window ──────────────────────────────────────────────────────────
298
  chatbot = gr.Chatbot(
299
- value=[],
300
- elem_id="chatbot-box",
301
- label="",
302
  height=460,
303
- bubble_full_width=False,
304
  show_label=False,
305
- avatar_images=(None, "https://api.dicebear.com/7.x/bottts-neutral/svg?seed=langgraph&backgroundColor=7c6af7"),
 
 
 
 
 
 
 
 
 
 
 
306
  )
307
 
308
- # ── Input row ────────────────────────────────────────────────────────────
309
- with gr.Row(elem_id="input-row"):
 
 
310
  msg = gr.Textbox(
311
- placeholder="Ask me anything…",
312
- show_label=False,
313
- lines=1,
314
- max_lines=5,
315
- elem_id="msg-input",
316
- scale=8,
317
  )
318
- send_btn = gr.Button("Send ↑", elem_id="send-btn", scale=1)
319
- clear_btn = gr.Button("Clear", elem_id="clear-btn", scale=1)
320
 
321
- # ── Footer ───────────────────────────────────────────────────────────────
322
- gr.HTML("""
323
- <div id="footer">
324
- Built with LangGraph Β· Groq Β· Gradio &nbsp;|&nbsp; Deploy on πŸ€— Hugging Face Spaces
325
- </div>
326
- """)
327
 
328
- # ── Events ───────────────────────────────────────────────────────────────
329
- send_btn.click(chat, [msg, chatbot], [msg, chatbot])
330
- msg.submit(chat, [msg, chatbot], [msg, chatbot])
331
- clear_btn.click(clear_chat, outputs=[msg, chatbot])
 
 
332
 
333
  if __name__ == "__main__":
334
  demo.launch(server_name="0.0.0.0", server_port=7860)
 
7
  from langchain_groq import ChatGroq
8
  from langchain_core.messages import HumanMessage, AIMessage, SystemMessage
9
 
10
+ # ── State ─────────────────────────────────────────────────────────────────────
 
11
  class State(TypedDict):
12
  messages: Annotated[list, add_messages]
13
 
14
+ # ── LLM ───────────────────────────────────────────────────────────────────────
 
 
 
 
 
15
  def get_llm():
16
+ return ChatGroq(
17
+ model="llama-3.3-70b-versatile",
18
+ api_key=os.environ.get("GROQ_API_KEY", ""),
19
+ temperature=0.7,
20
+ max_tokens=1024,
21
+ )
 
 
 
 
 
 
 
 
 
 
 
22
 
23
+ SYSTEM_PROMPT = SystemMessage(content=(
24
+ "You are NeuraChat β€” a brilliant, friendly general-purpose AI assistant. "
25
+ "Answer clearly and be concise yet thorough. Adapt your tone to the user: "
26
+ "casual for chat, precise for technical questions. "
27
+ "When asked to code, produce clean, well-commented examples. "
28
+ "Use markdown formatting where it helps readability."
29
+ ))
30
 
31
+ # ── LangGraph ──────────────────────────────────────────────────────────────────
32
  def build_graph():
33
+ llm = get_llm()
 
 
 
 
34
 
35
+ def chatbot_node(state: State) -> State:
36
+ response = llm.invoke([SYSTEM_PROMPT] + state["messages"])
37
+ return {"messages": [response]}
38
 
39
+ g = StateGraph(State)
40
+ g.add_node("chatbot", chatbot_node)
41
+ g.add_edge(START, "chatbot")
42
+ g.add_edge("chatbot", END)
43
+ return g.compile()
44
 
45
+ graph = build_graph()
46
+
47
+ # ── Chat handler ───────────────────────────────────────────────────────────────
48
  def chat(user_message: str, history: list):
 
49
  if not user_message.strip():
50
+ return history, ""
 
 
51
  lc_messages = []
52
+ for m in history:
53
+ cls = HumanMessage if m["role"] == "user" else AIMessage
54
+ lc_messages.append(cls(content=m["content"]))
 
55
  lc_messages.append(HumanMessage(content=user_message))
 
 
56
  result = graph.invoke({"messages": lc_messages})
57
  ai_reply = result["messages"][-1].content
58
+ return history + [
59
+ {"role": "user", "content": user_message},
60
+ {"role": "assistant", "content": ai_reply},
61
+ ], ""
62
 
63
  def clear_chat():
64
+ return [], ""
65
 
66
+ def use_example(ex):
67
+ return ex
68
 
69
+ # ── CSS ────────────────────────────────────────────────────────────────────────
70
  CSS = """
71
+ @import url('https://fonts.googleapis.com/css2?family=Syne:wght@700;800&family=DM+Sans:opsz,wght@9..40,300;9..40,400;9..40,500&display=swap');
72
+ *{box-sizing:border-box}
73
+ body,.gradio-container{background:#08080f!important;font-family:'DM Sans',sans-serif!important;color:#e8e6f0!important}
74
+ .gradio-container{max-width:880px!important;margin:0 auto!important}
75
+ .nc-header{text-align:center;padding:52px 24px 24px;position:relative}
76
+ .nc-header::before{content:'';position:absolute;top:-60px;left:50%;transform:translateX(-50%);width:500px;height:300px;background:radial-gradient(ellipse,rgba(124,58,237,.22) 0%,transparent 70%);pointer-events:none}
77
+ .nc-title{font-family:'Syne',sans-serif;font-size:3rem;font-weight:800;letter-spacing:-.04em;background:linear-gradient(135deg,#c084fc 0%,#818cf8 50%,#38bdf8 100%);-webkit-background-clip:text;-webkit-text-fill-color:transparent;background-clip:text;margin:0 0 6px;position:relative;z-index:1}
78
+ .nc-sub{font-size:.82rem;color:#4b5563;letter-spacing:.1em;text-transform:uppercase;position:relative;z-index:1}
79
+ .nc-tags{display:flex;justify-content:center;gap:8px;margin-bottom:20px;flex-wrap:wrap;padding:0 16px}
80
+ .nc-tag{background:rgba(124,58,237,.1);border:1px solid rgba(124,58,237,.22);color:#a78bfa;border-radius:100px;padding:4px 14px;font-size:.78rem;font-weight:500;letter-spacing:.04em}
81
+ #nc-chatbot{border:1px solid rgba(255,255,255,.07)!important;border-radius:20px!important;background:rgba(255,255,255,.025)!important;margin:0 16px!important;box-shadow:0 0 80px rgba(124,58,237,.07),inset 0 1px 0 rgba(255,255,255,.05)!important}
82
+ .message.user{background:linear-gradient(135deg,rgba(124,58,237,.32),rgba(99,102,241,.22))!important;border:1px solid rgba(124,58,237,.28)!important;color:#ede9fe!important;border-radius:16px 16px 4px 16px!important}
83
+ .message.bot{background:rgba(255,255,255,.04)!important;border:1px solid rgba(255,255,255,.08)!important;color:#d4d0e8!important;border-radius:16px 16px 16px 4px!important}
84
+ .message{font-size:.94rem!important;line-height:1.7!important}
85
+ .nc-chips{padding:14px 16px 4px;display:flex;gap:8px;flex-wrap:wrap}
86
+ .nc-chips button{background:rgba(255,255,255,.04)!important;border:1px solid rgba(255,255,255,.09)!important;border-radius:10px!important;color:#6b7280!important;font-family:'DM Sans',sans-serif!important;font-size:.81rem!important;padding:6px 14px!important;cursor:pointer!important;transition:all .18s!important;white-space:nowrap!important}
87
+ .nc-chips button:hover{background:rgba(124,58,237,.15)!important;border-color:rgba(124,58,237,.35)!important;color:#c4b5fd!important}
88
+ .nc-row{padding:12px 16px 28px!important;gap:10px!important;align-items:flex-end!important}
89
+ .nc-row textarea{background:rgba(255,255,255,.04)!important;border:1px solid rgba(255,255,255,.1)!important;border-radius:14px!important;color:#e8e6f0!important;font-family:'DM Sans',sans-serif!important;font-size:.94rem!important;padding:13px 16px!important;resize:none!important;transition:border-color .2s,box-shadow .2s!important}
90
+ .nc-row textarea:focus{border-color:rgba(124,58,237,.55)!important;box-shadow:0 0 0 3px rgba(124,58,237,.1)!important;outline:none!important}
91
+ .nc-row textarea::placeholder{color:#374151!important}
92
+ #nc-send,#nc-clear{border-radius:12px!important;font-family:'Syne',sans-serif!important;font-weight:700!important;font-size:.86rem!important;border:none!important;height:46px!important;cursor:pointer!important;transition:all .2s!important}
93
+ #nc-send{background:linear-gradient(135deg,#7c3aed,#6366f1)!important;color:#fff!important;min-width:90px!important;box-shadow:0 4px 18px rgba(124,58,237,.4)!important}
94
+ #nc-send:hover{transform:translateY(-1px) scale(1.02)!important;box-shadow:0 6px 28px rgba(124,58,237,.55)!important}
95
+ #nc-clear{background:transparent!important;color:#4b5563!important;border:1px solid rgba(255,255,255,.08)!important;min-width:74px!important}
96
+ #nc-clear:hover{color:#6b7280!important}
97
+ .nc-footer{text-align:center;padding:18px;font-size:.74rem;color:#1f2937;letter-spacing:.06em}
98
+ ::-webkit-scrollbar{width:3px}::-webkit-scrollbar-track{background:transparent}::-webkit-scrollbar-thumb{background:rgba(124,58,237,.25);border-radius:2px}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
99
  """
100
 
101
+ EXAMPLES = [
102
+ "✨ What can you help me with?",
103
+ "🧠 Explain quantum entanglement simply",
104
+ "πŸ’» Write a Python web scraper",
105
+ "πŸ“ Draft a professional email",
106
+ "🎨 Give me a creative writing prompt",
107
+ "πŸ”’ Integrate xΒ² from 0 to 3",
108
+ ]
109
 
110
+ # ── UI ─────────────────────────────────────────────────────────────────────────
111
+ with gr.Blocks(css=CSS, title="NeuraChat", theme=gr.themes.Base()) as demo:
112
+
113
+ gr.HTML("""
114
+ <div class="nc-header">
115
+ <div class="nc-title">NeuraChat</div>
116
+ <div class="nc-sub">LangGraph &nbsp;Β·&nbsp; Groq &nbsp;Β·&nbsp; Llama 3.3 70B</div>
117
+ </div>
118
+ <div class="nc-tags">
119
+ <span class="nc-tag">⚑ Ultra-fast inference</span>
120
+ <span class="nc-tag">πŸ”— LangGraph state</span>
121
+ <span class="nc-tag">πŸ¦™ Llama 3.3 70B</span>
122
+ <span class="nc-tag">✨ Markdown support</span>
123
+ </div>
124
+ """)
125
 
 
126
  chatbot = gr.Chatbot(
127
+ elem_id="nc-chatbot",
128
+ type="messages",
 
129
  height=460,
 
130
  show_label=False,
131
+ avatar_images=(
132
+ None,
133
+ "https://api.dicebear.com/9.x/bottts-neutral/svg?seed=neurachat&backgroundColor=7c3aed&radius=50",
134
+ ),
135
+ bubble_full_width=False,
136
+ render_markdown=True,
137
+ placeholder=(
138
+ "<div style='text-align:center;padding:60px 20px;color:#374151'>"
139
+ "<div style='font-size:2.4rem;margin-bottom:10px'>πŸ€–</div>"
140
+ "<div style='font-family:Syne,sans-serif;font-size:1.05rem;color:#4b5563'>Ask me anything</div>"
141
+ "</div>"
142
+ ),
143
  )
144
 
145
+ with gr.Row(elem_classes=["nc-chips"]):
146
+ chips = [gr.Button(ex, size="sm") for ex in EXAMPLES]
147
+
148
+ with gr.Row(elem_classes=["nc-row"]):
149
  msg = gr.Textbox(
150
+ placeholder="Type a message… (Enter to send)",
151
+ show_label=False, lines=1, max_lines=8,
152
+ scale=6, container=False,
 
 
 
153
  )
154
+ send_btn = gr.Button("Send ↑", elem_id="nc-send", variant="primary", scale=1)
155
+ clear_btn = gr.Button("Clear", elem_id="nc-clear", scale=1)
156
 
157
+ gr.HTML('<div class="nc-footer">NeuraChat &nbsp;Β·&nbsp; LangGraph + Groq + Gradio &nbsp;Β·&nbsp; Open Source</div>')
 
 
 
 
 
158
 
159
+ # Events
160
+ send_btn.click(chat, [msg, chatbot], [chatbot, msg])
161
+ msg.submit(chat, [msg, chatbot], [chatbot, msg])
162
+ clear_btn.click(clear_chat, outputs=[chatbot, msg])
163
+ for chip, ex in zip(chips, EXAMPLES):
164
+ chip.click(use_example, inputs=gr.State(ex), outputs=msg)
165
 
166
  if __name__ == "__main__":
167
  demo.launch(server_name="0.0.0.0", server_port=7860)
requirements.txt CHANGED
@@ -2,3 +2,4 @@ gradio>=4.44.0
2
  langgraph>=0.2.0
3
  langchain-groq>=0.2.0
4
  langchain-core>=0.3.0
 
 
2
  langgraph>=0.2.0
3
  langchain-groq>=0.2.0
4
  langchain-core>=0.3.0
5
+ typing-extensions>=4.8.0