Spaces:
Sleeping
Sleeping
QAway-to
commited on
Commit
Β·
8caee6c
1
Parent(s):
de51249
12333323
Browse files- app.py +132 -28
- llm_streaming.py +0 -20
- logic.py +0 -17
app.py
CHANGED
|
@@ -1,8 +1,120 @@
|
|
| 1 |
-
# app.py
|
| 2 |
import gradio as gr
|
| 3 |
-
|
| 4 |
-
|
|
|
|
|
|
|
| 5 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 6 |
with gr.Blocks() as demo:
|
| 7 |
gr.Markdown("# π§ AI Π°ΡΡΠΈΡΡΠ΅Π½Ρ Tradelink")
|
| 8 |
|
|
@@ -24,35 +136,27 @@ with gr.Blocks() as demo:
|
|
| 24 |
portfolio_input_pair_2 = gr.Textbox(label="ΠΠΎΡΡΡΠ΅Π»Ρ 2")
|
| 25 |
compare_button = gr.Button("π Π‘ΡΠ°Π²Π½ΠΈΡΡ")
|
| 26 |
|
| 27 |
-
|
| 28 |
-
|
| 29 |
-
|
| 30 |
-
|
| 31 |
-
return gr.update(visible=False), gr.update(visible=True), "ΠΡΠ±Π΅ΡΠΈΡΠ΅ Π΄Π²Π° ΠΏΠΎΡΡΡΠ΅Π»Ρ Π΄Π»Ρ ΡΡΠ°Π²Π½Π΅Π½ΠΈΡ."
|
| 32 |
-
elif btn_id == "passport":
|
| 33 |
-
return gr.update(visible=False), gr.update(visible=False), "ΠΠ°ΡΠΏΠΎΡΡ β ΡΡΠΎ ΠΏΡΠΎΠ΄ΡΠΊΡ Π΄Π»Ρ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ Π°ΠΊΡΠΈΠ²Π°ΠΌΠΈ..."
|
| 34 |
-
elif btn_id == "marketplace":
|
| 35 |
-
return gr.update(visible=False), gr.update(visible=False), "ΠΠ°ΡΠΊΠ΅ΡΠΏΠ»Π΅ΠΉΡ β ΠΊΠ°ΡΠ°Π»ΠΎΠ³ ΡΡΡΠ°ΡΠ΅Π³ΠΈΠΉ..."
|
| 36 |
-
elif btn_id == "tellme":
|
| 37 |
-
return gr.update(visible=False), gr.update(visible=False), "Π₯ΠΎΡΠΈΡΠ΅ ΡΠ°ΡΡΠΊΠ°ΠΆΡ ΠΏΡΠΎ Alpha, Beta, Sharpe?"
|
| 38 |
-
else:
|
| 39 |
-
return gr.update(visible=False), gr.update(visible=False), "ΠΡΠ±Π΅ΡΠΈΡΠ΅ Π΄Π΅ΠΉΡΡΠ²ΠΈΠ΅."
|
| 40 |
|
|
|
|
| 41 |
btn_analyze.click(lambda: on_button_click("analyze"), outputs=[analysis_block, compare_block, output])
|
| 42 |
btn_compare.click(lambda: on_button_click("compare"), outputs=[analysis_block, compare_block, output])
|
| 43 |
btn_passport.click(lambda: on_button_click("passport"), outputs=[analysis_block, compare_block, output])
|
| 44 |
btn_marketplace.click(lambda: on_button_click("marketplace"), outputs=[analysis_block, compare_block, output])
|
| 45 |
btn_tellme.click(lambda: on_button_click("tellme"), outputs=[analysis_block, compare_block, output])
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
if not metrics:
|
| 53 |
-
return "β ΠΠ΅ΡΡΠΈΠΊΠΈ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½Ρ."
|
| 54 |
-
metrics_text = ", ".join([f"{k}: {v}" for k, v in metrics.items()])
|
| 55 |
-
prompt = f"ΠΠΎΡ ΠΌΠ΅ΡΡΠΈΠΊΠΈ ΠΏΠΎΡΡΡΠ΅Π»Ρ: {metrics_text}. ΠΡΠΎΠ°Π½Π°Π»ΠΈΠ·ΠΈΡΡΠΉ Π½Π° ΡΡΡΡΠΊΠΎΠΌ ΡΠ·ΡΠΊΠ΅, ΠΊΠ°ΠΊ ΡΠΈΠ½Π°Π½ΡΠΎΠ²ΡΠΉ Π°Π½Π°Π»ΠΈΡΠΈΠΊ."
|
| 56 |
-
return stream_llm_response(prompt)
|
| 57 |
-
|
| 58 |
-
analyze_button.click(analyze_single, inputs=portfolio_input_single, outputs=output)
|
|
|
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
+
import requests
|
| 3 |
+
import re
|
| 4 |
+
from openai import OpenAI
|
| 5 |
+
from typing import Generator
|
| 6 |
|
| 7 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 8 |
+
# π ΠΠ°ΡΡΡΠΎΠΉΠΊΠ° API ΠΊΠ»ΠΈΠ΅Π½ΡΠ° (Featherless.ai)
|
| 9 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 10 |
+
client = OpenAI(
|
| 11 |
+
base_url="https://api.featherless.ai/v1",
|
| 12 |
+
api_key="rc_2b641fdc25954733a5ea0fca262626daf21562b4b"
|
| 13 |
+
)
|
| 14 |
+
|
| 15 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 16 |
+
# π ΠΡΠΏΠΎΠΌΠΎΠ³Π°ΡΠ΅Π»ΡΠ½ΡΠ΅ ΡΡΠ½ΠΊΡΠΈΠΈ
|
| 17 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 18 |
+
def extract_portfolio_id(text):
|
| 19 |
+
match = re.search(r"[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}", text)
|
| 20 |
+
return match.group(0) if match else None
|
| 21 |
+
|
| 22 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 23 |
+
# π ΠΠ½Π°Π»ΠΈΠ· ΠΎΠ΄Π½ΠΎΠ³ΠΎ ΠΏΠΎΡΡΡΠ΅Π»Ρ
|
| 24 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 25 |
+
def analyze_from_api(portfolio_input: str) -> Generator[str, None, None]:
|
| 26 |
+
try:
|
| 27 |
+
portfolio_id = extract_portfolio_id(portfolio_input)
|
| 28 |
+
if not portfolio_id:
|
| 29 |
+
yield "β ΠΠ΅Π²Π΅ΡΠ½ΡΠΉ ΡΠΎΡΠΌΠ°Ρ portfolioId."
|
| 30 |
+
return
|
| 31 |
+
|
| 32 |
+
url = f"https://api.tradelink.pro/portfolio/get?portfolioId={portfolio_id}&extended=1&declaration=1&step=day&lang=en&incViews=1"
|
| 33 |
+
response = requests.get(url)
|
| 34 |
+
json_data = response.json()
|
| 35 |
+
extended = json_data.get("data", {}).get("extended", {})
|
| 36 |
+
|
| 37 |
+
metrics_keys = ["alphaRatio", "betaRatio", "cagr", "sharpe", "sortino", "volatility"]
|
| 38 |
+
filtered = {k: v for k, v in extended.items() if k in metrics_keys and isinstance(v, (int, float))}
|
| 39 |
+
|
| 40 |
+
if not filtered:
|
| 41 |
+
yield "β ΠΠ΅ΡΡΠΈΠΊΠΈ Π½Π΅ Π½Π°ΠΉΠ΄Π΅Π½Ρ ΠΈΠ»ΠΈ ΠΎΡΡΡΡΡΡΠ²ΡΡΡ."
|
| 42 |
+
return
|
| 43 |
+
|
| 44 |
+
metrics_text = ", ".join([f"{k}: {v}" for k, v in filtered.items()])
|
| 45 |
+
prompt = f"""ΠΠΎΡ ΠΌΠ΅ΡΡΠΈΠΊΠΈ ΠΏΠΎΡΡΡΠ΅Π»Ρ: {metrics_text}.
|
| 46 |
+
ΠΡΠΎΠ°Π½Π°Π»ΠΈΠ·ΠΈΡΡΠΉ ΠΈΡ
ΠΈ ΠΎΠ±ΡΡΡΠ½ΠΈ ΡΠΈΠ»ΡΠ½ΡΠ΅ ΠΈ ΡΠ»Π°Π±ΡΠ΅ ΡΡΠΎΡΠΎΠ½Ρ. ΠΡΠ²Π΅ΡΠ°ΠΉ Π½Π° ΡΡΡΡΠΊΠΎΠΌ ΡΠ·ΡΠΊΠ΅ ΠΊΠ°ΠΊ ΡΠΈΠ½Π°Π½ΡΠΎΠ²ΡΠΉ Π°Π½Π°Π»ΠΈΡΠΈΠΊ."""
|
| 47 |
+
|
| 48 |
+
response = client.chat.completions.create(
|
| 49 |
+
model="meta-llama/Meta-Llama-3.1-8B-Instruct",
|
| 50 |
+
messages=[{"role": "system", "content": "Π’Ρ β ΡΠΈΠ½Π°Π½ΡΠΎΠ²ΡΠΉ Π°Π½Π°Π»ΠΈΡΠΈΠΊ."},
|
| 51 |
+
{"role": "user", "content": prompt}],
|
| 52 |
+
stream=True
|
| 53 |
+
)
|
| 54 |
+
|
| 55 |
+
partial = ""
|
| 56 |
+
for chunk in response:
|
| 57 |
+
delta = chunk.choices[0].delta.content
|
| 58 |
+
if delta:
|
| 59 |
+
partial += delta
|
| 60 |
+
yield partial
|
| 61 |
+
|
| 62 |
+
except Exception as e:
|
| 63 |
+
yield f"β ΠΡΠΈΠ±ΠΊΠ°: {e}"
|
| 64 |
+
|
| 65 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 66 |
+
# π¬ Π Π°ΡΡΠΊΠ°ΠΆΠΈ / ΠΠΈΠ°Π»ΠΎΠ³
|
| 67 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 68 |
+
chat_history = [{"role": "system", "content": "Π’Ρ β ΡΠΈΠ½Π°Π½ΡΠΎΠ²ΡΠΉ Π°Π½Π°Π»ΠΈΡΠΈΠΊ. ΠΡΠ²Π΅ΡΠ°ΠΉ ΠΊΡΠ°ΡΠΊΠΎ ΠΈ ΠΏΠΎΠ½ΡΡΠ½ΠΎ."}]
|
| 69 |
+
|
| 70 |
+
def handle_tellme_stream() -> Generator[str, None, None]:
|
| 71 |
+
chat_history.append({"role": "user", "content": "Π Π°ΡΡΠΊΠ°ΠΆΠΈ ΠΏΡΠΎ Alpha, Sharpe ΠΈΠ»ΠΈ Beta"})
|
| 72 |
+
response = client.chat.completions.create(
|
| 73 |
+
model="meta-llama/Meta-Llama-3.1-8B-Instruct",
|
| 74 |
+
messages=chat_history,
|
| 75 |
+
stream=True
|
| 76 |
+
)
|
| 77 |
+
|
| 78 |
+
collected = ""
|
| 79 |
+
for chunk in response:
|
| 80 |
+
if chunk.choices and chunk.choices[0].delta.get("content"):
|
| 81 |
+
token = chunk.choices[0].delta["content"]
|
| 82 |
+
collected += token
|
| 83 |
+
yield token
|
| 84 |
+
|
| 85 |
+
chat_history.append({"role": "assistant", "content": collected})
|
| 86 |
+
|
| 87 |
+
def followup_reply(user_input):
|
| 88 |
+
chat_history.append({"role": "user", "content": user_input})
|
| 89 |
+
response = client.chat.completions.create(
|
| 90 |
+
model="meta-llama/Meta-Llama-3.1-8B-Instruct",
|
| 91 |
+
messages=chat_history,
|
| 92 |
+
stream=False
|
| 93 |
+
)
|
| 94 |
+
answer = response.choices[0].message.content
|
| 95 |
+
chat_history.append({"role": "assistant", "content": answer})
|
| 96 |
+
return answer
|
| 97 |
+
|
| 98 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 99 |
+
# π§ ΠΠ°Π²ΠΈΠ³Π°ΡΠΈΠΎΠ½Π½ΡΠ΅ ΡΡΠ΅Π½Π°ΡΠΈΠΈ
|
| 100 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 101 |
+
def on_button_click(btn_id):
|
| 102 |
+
if btn_id == "analyze":
|
| 103 |
+
return gr.update(visible=True), gr.update(visible=False), "ΠΡΠ±Π΅ΡΠΈΡΠ΅ ΠΏΠΎΡΡΡΠ΅Π»Ρ Π΄Π»Ρ Π°Π½Π°Π»ΠΈΠ·Π°."
|
| 104 |
+
elif btn_id == "compare":
|
| 105 |
+
return gr.update(visible=False), gr.update(visible=True), "ΠΡΠ±Π΅ΡΠΈΡΠ΅ Π΄Π²Π° ΠΏΠΎΡΡΡΠ΅Π»Ρ Π΄Π»Ρ ΡΡΠ°Π²Π½Π΅Π½ΠΈΡ."
|
| 106 |
+
elif btn_id == "passport":
|
| 107 |
+
return gr.update(visible=False), gr.update(visible=False), "ΠΠ°ΡΠΏΠΎΡΡ β ΡΡΠΎ ΠΏΡΠΎΠ΄ΡΠΊΡ Π΄Π»Ρ ΡΠΏΡΠ°Π²Π»Π΅Π½ΠΈΡ Π°ΠΊΡΠΈΠ²Π°ΠΌΠΈ..."
|
| 108 |
+
elif btn_id == "marketplace":
|
| 109 |
+
return gr.update(visible=False), gr.update(visible=False), "ΠΠ°ΡΠΊΠ΅ΡΠΏΠ»Π΅ΠΉΡ β ΠΊΠ°ΡΠ°Π»ΠΎΠ³ ΡΡΡΠ°ΡΠ΅Π³ΠΈΠΉ..."
|
| 110 |
+
elif btn_id == "tellme":
|
| 111 |
+
return gr.update(visible=False), gr.update(visible=False), None
|
| 112 |
+
else:
|
| 113 |
+
return gr.update(visible=False), gr.update(visible=False), ""
|
| 114 |
+
|
| 115 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 116 |
+
# ποΈ ΠΠ½ΡΠ΅ΡΡΠ΅ΠΉΡ (Gradio Blocks)
|
| 117 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 118 |
with gr.Blocks() as demo:
|
| 119 |
gr.Markdown("# π§ AI Π°ΡΡΠΈΡΡΠ΅Π½Ρ Tradelink")
|
| 120 |
|
|
|
|
| 136 |
portfolio_input_pair_2 = gr.Textbox(label="ΠΠΎΡΡΡΠ΅Π»Ρ 2")
|
| 137 |
compare_button = gr.Button("π Π‘ΡΠ°Π²Π½ΠΈΡΡ")
|
| 138 |
|
| 139 |
+
with gr.Column(visible=False) as tellme_block:
|
| 140 |
+
gr.Markdown("## π¬ ΠΠ½ΡΠ΅ΡΠ°ΠΊΡΠΈΠ²Π½ΡΠΉ Π΄ΠΈΠ°Π»ΠΎΠ³")
|
| 141 |
+
textbox_followup = gr.Textbox(label="ΠΠ°Ρ ΠΎΡΠ²Π΅Ρ")
|
| 142 |
+
button_followup = gr.Button("ΠΡΠ²Π΅ΡΠΈΡΡ")
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 143 |
|
| 144 |
+
# ΠΠ±ΡΠ°Π±ΠΎΡΡΠΈΠΊΠΈ ΠΊΠ½ΠΎΠΏΠΎΠΊ Π½Π°Π²ΠΈΠ³Π°ΡΠΈΠΈ
|
| 145 |
btn_analyze.click(lambda: on_button_click("analyze"), outputs=[analysis_block, compare_block, output])
|
| 146 |
btn_compare.click(lambda: on_button_click("compare"), outputs=[analysis_block, compare_block, output])
|
| 147 |
btn_passport.click(lambda: on_button_click("passport"), outputs=[analysis_block, compare_block, output])
|
| 148 |
btn_marketplace.click(lambda: on_button_click("marketplace"), outputs=[analysis_block, compare_block, output])
|
| 149 |
btn_tellme.click(lambda: on_button_click("tellme"), outputs=[analysis_block, compare_block, output])
|
| 150 |
+
btn_tellme.click(fn=handle_tellme_stream, outputs=output)
|
| 151 |
+
|
| 152 |
+
# ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° Π°Π½Π°Π»ΠΈΠ·Π° ΠΏΠΎΡΡΡΠ΅Π»Ρ
|
| 153 |
+
analyze_button.click(fn=analyze_from_api, inputs=portfolio_input_single, outputs=output)
|
| 154 |
+
|
| 155 |
+
# ΠΠ±ΡΠ°Π±ΠΎΡΠΊΠ° Π΄ΠΈΠ°Π»ΠΎΠ³Π° "Π Π°ΡΡΠΊΠ°ΠΆΠΈ"
|
| 156 |
+
button_followup.click(fn=followup_reply, inputs=textbox_followup, outputs=output)
|
| 157 |
|
| 158 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 159 |
+
# π ΠΠ°ΠΏΡΡΠΊ
|
| 160 |
+
# βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
|
| 161 |
+
if __name__ == "__main__":
|
| 162 |
+
demo.launch()
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
llm_streaming.py
DELETED
|
@@ -1,20 +0,0 @@
|
|
| 1 |
-
# llm_streaming.py
|
| 2 |
-
from openai import OpenAI
|
| 3 |
-
|
| 4 |
-
client = OpenAI(
|
| 5 |
-
base_url="https://api.featherless.ai/v1",
|
| 6 |
-
api_key="rc_2b641fdc25954733a5ef7ccd10ce6a8e1db603e386a0fca262626daf21562b4b"
|
| 7 |
-
)
|
| 8 |
-
|
| 9 |
-
def stream_llm_response(prompt):
|
| 10 |
-
stream = client.chat.completions.create(
|
| 11 |
-
model="meta-llama/Meta-Llama-3.1-8B-Instruct",
|
| 12 |
-
messages=[
|
| 13 |
-
{"role": "system", "content": "Π’Ρ β ΡΠΈΠ½Π°Π½ΡΠΎΠ²ΡΠΉ Π°Π½Π°Π»ΠΈΡΠΈΠΊ."},
|
| 14 |
-
{"role": "user", "content": prompt}
|
| 15 |
-
],
|
| 16 |
-
stream=True
|
| 17 |
-
)
|
| 18 |
-
for chunk in stream:
|
| 19 |
-
if "choices" in chunk.model_dump() and chunk.choices[0].delta.content:
|
| 20 |
-
yield chunk.choices[0].delta.content
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
logic.py
DELETED
|
@@ -1,17 +0,0 @@
|
|
| 1 |
-
# logic.py
|
| 2 |
-
|
| 3 |
-
import requests
|
| 4 |
-
import json
|
| 5 |
-
import re
|
| 6 |
-
|
| 7 |
-
def extract_portfolio_id(raw_input: str) -> str:
|
| 8 |
-
match = re.search(r"([a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12})", raw_input)
|
| 9 |
-
return match.group(1) if match else None
|
| 10 |
-
|
| 11 |
-
def fetch_portfolio_metrics(portfolio_id: str) -> dict:
|
| 12 |
-
url = f"https://api.tradelink.pro/portfolio/get?portfolioId={portfolio_id}&extended=1"
|
| 13 |
-
response = requests.get(url)
|
| 14 |
-
json_data = response.json()
|
| 15 |
-
extended = json_data.get("data", {}).get("extended", {})
|
| 16 |
-
metrics_keys = ["alphaRatio", "betaRatio", "cagr", "sharpe", "sortino", "volatility"]
|
| 17 |
-
return {k: v for k, v in extended.items() if k in metrics_keys and isinstance(v, (int, float))}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|