Nullpointer-KK commited on
Commit
2fcbd3b
·
1 Parent(s): 22e6cef

update space 3

Browse files
Files changed (2) hide show
  1. app.py +230 -55
  2. requirements.txt +15 -0
app.py CHANGED
@@ -1,70 +1,245 @@
1
- import gradio as gr
2
- from huggingface_hub import InferenceClient
 
3
 
 
 
 
4
 
5
- def respond(
6
- message,
7
- history: list[dict[str, str]],
8
- system_message,
9
- max_tokens,
10
- temperature,
11
- top_p,
12
- hf_token: gr.OAuthToken,
13
- ):
14
- """
15
- 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
16
- """
17
- client = InferenceClient(token=hf_token.token, model="openai/gpt-oss-20b")
18
 
19
- messages = [{"role": "system", "content": system_message}]
 
 
20
 
21
- messages.extend(history)
 
 
 
 
 
 
22
 
23
- messages.append({"role": "user", "content": message})
 
 
 
 
 
 
24
 
25
- response = ""
 
 
 
 
 
 
26
 
27
- for message in client.chat_completion(
28
- messages,
29
- max_tokens=max_tokens,
30
- stream=True,
31
- temperature=temperature,
32
- top_p=top_p,
33
- ):
34
- choices = message.choices
35
- token = ""
36
- if len(choices) and choices[0].delta.content:
37
- token = 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
- chatbot = gr.ChatInterface(
47
- respond,
48
- type="messages",
49
- additional_inputs=[
50
- gr.Textbox(value="You are a friendly Chatbot.", label="System message"),
51
- gr.Slider(minimum=1, maximum=2048, value=512, step=1, label="Max new tokens"),
52
- gr.Slider(minimum=0.1, maximum=4.0, value=0.7, step=0.1, label="Temperature"),
53
- gr.Slider(
54
- minimum=0.1,
55
- maximum=1.0,
56
- value=0.95,
57
- step=0.05,
58
- label="Top-p (nucleus sampling)",
59
- ),
60
- ],
61
- )
62
-
63
- with gr.Blocks() as demo:
64
- with gr.Sidebar():
65
- gr.LoginButton()
66
- chatbot.render()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
67
 
 
 
68
 
69
  if __name__ == "__main__":
 
70
  demo.launch()
 
1
+ import os, gradio as gr
2
+ from rag.pipeline import CryptoRAGPipeline
3
+ from rag.tools import get_price, get_fear_greed
4
 
5
+ pipe: CryptoRAGPipeline | None = None
6
+ DEFAULT_DENSE = "sentence-transformers/all-MiniLM-L6-v2"
7
+ DEFAULT_RERANK = "cross-encoder/ms-marco-MiniLM-L-6-v2"
8
 
9
+ def _ensure_pipe(dense_model: str | None = None, reranker_model: str | None = None):
10
+ global pipe
11
+ if pipe is None:
12
+ pipe = CryptoRAGPipeline(
13
+ dense_model=dense_model or DEFAULT_DENSE,
14
+ reranker_model=reranker_model or DEFAULT_RERANK
15
+ )
16
+ return pipe
 
 
 
 
 
17
 
18
+ def setup_pipeline(dense_model, reranker_model):
19
+ _ensure_pipe(dense_model, reranker_model)
20
+ return "✅ Pipeline initialised."
21
 
22
+ def add_openai_key(key):
23
+ p = _ensure_pipe()
24
+ key = (key or "").strip()
25
+ if not key:
26
+ return "Please paste an OpenAI API key"
27
+ p.set_openai(key)
28
+ return "🔐 OpenAI key set (not stored on disk)."
29
 
30
+ def add_files(files):
31
+ p = _ensure_pipe()
32
+ paths = [f.name for f in (files or [])]
33
+ if not paths:
34
+ return "No files uploaded."
35
+ p.add_local_files(paths)
36
+ return f"📄 Added {len(paths)} file(s)."
37
 
38
+ def add_urls(urls_text):
39
+ p = _ensure_pipe()
40
+ urls = [u.strip() for u in (urls_text or "").splitlines() if u.strip()]
41
+ if not urls:
42
+ return "No URLs provided."
43
+ p.add_urls(urls)
44
+ return f"🔗 Added {len(urls)} URL(s)."
45
 
46
+ def build_index():
47
+ p = _ensure_pipe()
48
+ p.build()
49
+ return "🧱 Index built (hybrid: BM25 + Dense)."
 
 
 
 
 
 
 
50
 
51
+ #def answer(query, k, alpha, top_k_rerank, filter_coin, stream_enable, model):
52
+ def answer(query, k, alpha, top_k_rerank, stream_enable, model):
53
+ p = _ensure_pipe()
54
+
55
+ try:
56
+ result = p.ask(
57
+ query, k=int(k), alpha=float(alpha),
58
+ top_k_rerank=int(top_k_rerank),
59
+ filters=None, stream=stream_enable
60
+ )
61
+ except Exception as e:
62
+ yield f"❌ Error while routing: {e}"
63
+ return
64
 
65
+ # Tool route (non-stream)
66
+ if result["route"] == "tools":
67
+ # Auto-detect coin from the user's query and show its price.
68
+ from rag.tools import get_price_any, get_price_multi, get_fear_greed
69
+ try:
70
+ coin_id, price = get_price_any(query, "usd")
71
+ except Exception as e:
72
+ yield f"🔧 Tool route: error resolving coin/price — {e}"
73
+ return
74
 
75
+ # Always include Fear & Greed (market mood)
76
+ parts = []
77
+ if price is not None:
78
+ parts.append(f"{coin_id} price ≈ ${price}")
79
+ else:
80
+ parts.append(f"{coin_id} price unavailable")
81
+
82
+ try:
83
+ fng = get_fear_greed()
84
+ if fng:
85
+ parts.append(f"Fear&Greed: {fng.get('value')} – {fng.get('value_classification')}")
86
+ except Exception:
87
+ pass
88
+
89
+ # (Optional) If user didn’t specify a coin clearly, also show a quick trio: ETH, SOL, XRP
90
+ if coin_id not in {"ethereum", "solana", "ripple"} and any(w in query.lower() for w in ["price", "quote"]):
91
+ try:
92
+ batch = get_price_multi(["ethereum", "solana", "ripple"], "usd")
93
+ trio = []
94
+ if "ethereum" in batch and "usd" in batch["ethereum"]:
95
+ trio.append(f"ETH ${batch['ethereum']['usd']}")
96
+ if "solana" in batch and "usd" in batch["solana"]:
97
+ trio.append(f"SOL ${batch['solana']['usd']}")
98
+ if "ripple" in batch and "usd" in batch["ripple"]:
99
+ trio.append(f"XRP ${batch['ripple']['usd']}")
100
+ if trio:
101
+ parts.append("Also: " + " | ".join(trio))
102
+ except Exception:
103
+ pass
104
+
105
+ yield "🔧 " + " | ".join(parts)
106
+ return
107
+
108
+
109
+ # Retrieval not ready / no results
110
+ if result["route"] == "not_ready":
111
+ reason = result.get("reason")
112
+ if reason == "index_empty":
113
+ yield "⚠️ Your knowledge base is empty. Upload PDF/TXT/MD or add URLs, then click **Build Index**."
114
+ elif reason == "build_failed":
115
+ yield "⚠️ Index not built. Try clicking **Build Index** (after adding docs/URLs)."
116
+ elif reason == "no_results":
117
+ yield "🤔 No matches retrieved. Try a simpler query, different keywords, or ingest more sources; then rebuild."
118
+ else:
119
+ yield "⚠️ Retrieval not ready. Please ingest and build."
120
+ return
121
+
122
+ # RAG route
123
+ contexts = result["contexts"]
124
+
125
+ # Stream tokens → progressively yield the growing string
126
+ if stream_enable:
127
+ full = ""
128
+ try:
129
+ for token in p.answer_stream(query, contexts, model=model):
130
+ full += token
131
+ yield full
132
+ except Exception as e:
133
+ yield f"❌ Error while streaming: {e}"
134
+ return
135
+ else:
136
+ # Non-streaming fallback (join all tokens)
137
+ try:
138
+ text = "".join(p.answer_stream(query, contexts, model=model))
139
+ except Exception as e:
140
+ yield f"❌ Error while generating: {e}"
141
+ return
142
+ yield text
143
+
144
+ def _push_status(msg: str, history: list[str] | None, keep: int = 10):
145
+ # 1 line per message; strip newlines
146
+ line = (msg or "").strip().replace("\n", " ")
147
+ hist = (history or []) + [line]
148
+ hist = hist[-keep:] # keep last 5
149
+ text = "\n".join(hist) # render as multi-line
150
+ return hist, text
151
+
152
+ # Wrappers that call your original functions and push into the rolling buffer
153
+ def setup_pipeline_s(dense_model, reranker_model, history):
154
+ msg = setup_pipeline(dense_model, reranker_model)
155
+ return _push_status(msg, history)
156
+
157
+ def add_openai_key_s(key, history):
158
+ msg = add_openai_key(key)
159
+ return _push_status(msg, history)
160
+
161
+ def add_files_s(files, history):
162
+ msg = add_files(files)
163
+ return _push_status(msg, history)
164
+
165
+ def add_urls_s(urls_text, history):
166
+ msg = add_urls(urls_text)
167
+ return _push_status(msg, history)
168
+
169
+ def build_index_s(history):
170
+ msg = build_index()
171
+ return _push_status(msg, history)
172
+
173
+ def on_load_s(history):
174
+ # If you want MANUAL init, return a neutral line here instead
175
+ return _push_status("👋 Ready. Click 'Initialize pipeline' to begin.", history)
176
+
177
+ with gr.Blocks(
178
+ title="Crypto RAG Chatbot",
179
+ css="""
180
+ #status-box { border: 1px solid #e5e7eb; border-radius: 10px; padding: 10px; margin-top: 12px; }
181
+ #status-body { white-space: pre-wrap; line-height: 1.25; max-height: calc(1.25em * 5 + 12px); overflow: auto; }
182
  """
183
+ ) as demo:
184
+ gr.Markdown(
185
+ "# 🟠 Crypto RAG Chatbot:<br>"
186
+ "<span style='font-size:0.95rem; line-height:1.4;'>"
187
+ "Step 1: click Initialize pipeline, enter OpenAI Key,Step 2: Upload documents and Paste links,Step 3: Build Index, Step 4: Ask away<br>"
188
+ "</span>"
189
+ )
190
+
191
+ with gr.Row():
192
+ with gr.Column(scale=1):
193
+ gr.Markdown("### 1) Init & Keys")
194
+ dense = gr.Textbox(value=DEFAULT_DENSE, label="Embedding model")
195
+ rerank = gr.Textbox(value=DEFAULT_RERANK, label="Reranker model")
196
+ btn_init = gr.Button("Initialize pipeline")
197
+ #status = gr.Markdown("...")
198
+ key = gr.Textbox(type="password", label="OpenAI API Key (required for chat)")
199
+ btn_key = gr.Button("Set OpenAI Key")
200
+
201
+ gr.Markdown("### 2) Ingest Data")
202
+ files = gr.File(label="Upload .pdf / .txt / .md", file_count="multiple")
203
+ btn_files = gr.Button("Add files")
204
+ urls = gr.Textbox(lines=3, label="URLs (one per line)")
205
+ btn_urls = gr.Button("Add URLs")
206
+ btn_build = gr.Button("3) Build Index")
207
+
208
+ gr.Markdown("### 3) Query Settings")
209
+ k = gr.Slider(2, 15, value=8, step=1, label="Top-K retrieve")
210
+ alpha = gr.Slider(0, 1, value=0.5, step=0.05, label="Hybrid alpha (BM25↔Dense)")
211
+ topk_rerank = gr.Slider(1, 10, value=5, step=1, label="Top-K after reranker")
212
+ #filter_coin = gr.Textbox(value="", label="Metadata filter: coin (optional)")
213
+ stream_toggle = gr.Checkbox(value=True, label="Streaming")
214
+ model = gr.Textbox(value="gpt-4o-mini", label="Chat model")
215
+
216
+ with gr.Column(scale=2):
217
+ # NEW: wider status in the chat column
218
+ #status = gr.Markdown("...", elem_id="status-banner")
219
+ gr.Markdown("### 4) Chat")
220
+ q = gr.Textbox(label="Ask a crypto question", lines=2)
221
+ btn_ask = gr.Button("Ask")
222
+ a = gr.Markdown("...")
223
+ with gr.Group(elem_id="status-box"):
224
+ gr.Markdown("**Status showing below (last 10 statuses):**")
225
+ status = gr.Markdown("...", elem_id="status-body")
226
+
227
+ status_state = gr.State([])
228
+
229
+ # on load
230
+ # remove auto load
231
+ # demo.load(on_load_s, [status_state], [status_state, status])
232
+
233
+ # init / keys / ingest / build → use the “_s” wrappers
234
+ btn_init.click( setup_pipeline_s, [dense, rerank, status_state], [status_state, status] )
235
+ btn_key.click( add_openai_key_s, [key, status_state], [status_state, status] )
236
+ btn_files.click(add_files_s, [files, status_state], [status_state, status] )
237
+ btn_urls.click( add_urls_s, [urls, status_state], [status_state, status] )
238
+ btn_build.click(build_index_s, [status_state], [status_state, status] )
239
 
240
+ # chat output remains the same (streams into `a`)
241
+ btn_ask.click(answer, [q, k, alpha, topk_rerank, stream_toggle, model], [a])
242
 
243
  if __name__ == "__main__":
244
+ # Set share=True if you want a public link locally
245
  demo.launch()
requirements.txt ADDED
@@ -0,0 +1,15 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ gradio>=4.44.0
2
+ openai>=1.40.0
3
+ tiktoken>=0.7.0
4
+ numpy>=1.26.0
5
+ pandas>=2.2.0
6
+ faiss-cpu>=1.8.0
7
+ sentence-transformers>=3.0.1
8
+ rank-bm25>=0.2.2
9
+ pypdf>=4.2.0
10
+ markdownify>=0.12.1
11
+ trafilatura>=1.9.0
12
+ uvicorn>=0.30.0
13
+ pydantic>=2.8.0
14
+ datasets>=2.20.0
15
+ ragas>=0.1.14