Spaces:
Running
Running
QAway-to
commited on
Commit
·
0d9474a
1
Parent(s):
135a260
Refactor project into layered prototype structure
Browse files- README.md +11 -0
- app.py +16 -10
- application/__init__.py +13 -0
- core/chat.py → application/chat_assistant.py +2 -1
- core/metrics.py → application/metrics_table.py +4 -2
- core/analyzer.py → application/portfolio_analyzer.py +3 -2
- core/comparer.py → application/portfolio_comparer.py +3 -2
- core/__init__.py +13 -2
- domain/__init__.py +3 -0
- infrastructure/__init__.py +18 -0
- {services → infrastructure}/llm_client.py +0 -0
- infrastructure/market_data/__init__.py +5 -0
- core/data_binance.py → infrastructure/market_data/binance.py +0 -0
- core/data_coinlore.py → infrastructure/market_data/coinlore.py +0 -0
- core/data_yfinance.py → infrastructure/market_data/yfinance.py +0 -0
- {services → infrastructure}/output_api.py +0 -0
- presentation/__init__.py +5 -0
- presentation/components/__init__.py +12 -0
- {core → presentation/components}/comparison_table.py +5 -3
- {core → presentation/components}/crypto_dashboard.py +3 -1
- {core → presentation/components}/multi_charts.py +0 -0
- {core → presentation/components}/visual_comparison.py +0 -0
- {core → presentation/components}/visualization.py +0 -0
- presentation/styles/__init__.py +5 -0
- presentation/styles/themes/__init__.py +3 -0
- {core/styles → presentation/styles/themes}/base.css +0 -0
- {core/styles → presentation/styles/themes}/crypto_dashboard.css +0 -0
- {core/styles → presentation/styles/themes}/multi_charts.css +0 -0
- {core → presentation/styles}/ui_style.css +0 -0
- services/__init__.py +0 -2
README.md
CHANGED
|
@@ -11,3 +11,14 @@ license: apache-2.0
|
|
| 11 |
---
|
| 12 |
|
| 13 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 11 |
---
|
| 12 |
|
| 13 |
Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
|
| 14 |
+
|
| 15 |
+
## Repository layout
|
| 16 |
+
|
| 17 |
+
The prototype is now organized into lightweight layers to keep responsibilities clear even in a demo setting:
|
| 18 |
+
|
| 19 |
+
- `application/` – orchestration services that combine prompts with infrastructure adapters.
|
| 20 |
+
- `infrastructure/` – clients for external APIs and market data providers (Featherless, Coinlore, etc.).
|
| 21 |
+
- `presentation/` – Gradio components, dashboards, and CSS themes displayed in the Space UI.
|
| 22 |
+
- `domain/` – placeholder for future data models specific to the investment analytics domain.
|
| 23 |
+
|
| 24 |
+
`app.py` wires these pieces together to expose the multi-tab Gradio experience on Hugging Face Spaces.
|
app.py
CHANGED
|
@@ -1,11 +1,17 @@
|
|
| 1 |
import gradio as gr
|
| 2 |
-
|
| 3 |
-
from
|
| 4 |
-
from
|
| 5 |
-
from
|
| 6 |
-
from
|
| 7 |
-
from
|
| 8 |
-
from
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
# === CSS loader ===
|
| 11 |
def load_css(path: str) -> str:
|
|
@@ -13,8 +19,8 @@ def load_css(path: str) -> str:
|
|
| 13 |
return f.read()
|
| 14 |
|
| 15 |
# === Styles ===
|
| 16 |
-
base_css
|
| 17 |
-
crypto_css = load_css("
|
| 18 |
|
| 19 |
# === Model setup ===
|
| 20 |
MODEL_NAME = "meta-llama/Meta-Llama-3.1-8B-Instruct"
|
|
@@ -44,7 +50,7 @@ with gr.Blocks(css=base_css) as demo:
|
|
| 44 |
|
| 45 |
# --- Comparison Table ---
|
| 46 |
with gr.TabItem("Comparison Table"):
|
| 47 |
-
from
|
| 48 |
pid_a = gr.Textbox(label="Portfolio A", value="3852a354-e66e-4bc5-97e9-55124e31e687")
|
| 49 |
pid_b = gr.Textbox(label="Portfolio B", value="b1ef37aa-5b9a-41b4-8823f2de36bb")
|
| 50 |
compare_btn = gr.Button("Load Comparison", variant="primary")
|
|
|
|
| 1 |
import gradio as gr
|
| 2 |
+
|
| 3 |
+
from application.chat_assistant import ChatAssistant
|
| 4 |
+
from application.metrics_table import show_metrics_table
|
| 5 |
+
from application.portfolio_analyzer import PortfolioAnalyzer
|
| 6 |
+
from application.portfolio_comparer import PortfolioComparer
|
| 7 |
+
from infrastructure.llm_client import llm_service
|
| 8 |
+
from presentation.components.crypto_dashboard import (
|
| 9 |
+
build_crypto_dashboard,
|
| 10 |
+
) # Plotly dashboard + KPI-line
|
| 11 |
+
from presentation.components.visual_comparison import (
|
| 12 |
+
build_price_chart,
|
| 13 |
+
build_volatility_chart,
|
| 14 |
+
) # Interactive pair comparison
|
| 15 |
|
| 16 |
# === CSS loader ===
|
| 17 |
def load_css(path: str) -> str:
|
|
|
|
| 19 |
return f.read()
|
| 20 |
|
| 21 |
# === Styles ===
|
| 22 |
+
base_css = load_css("presentation/styles/themes/base.css")
|
| 23 |
+
crypto_css = load_css("presentation/styles/themes/crypto_dashboard.css")
|
| 24 |
|
| 25 |
# === Model setup ===
|
| 26 |
MODEL_NAME = "meta-llama/Meta-Llama-3.1-8B-Instruct"
|
|
|
|
| 50 |
|
| 51 |
# --- Comparison Table ---
|
| 52 |
with gr.TabItem("Comparison Table"):
|
| 53 |
+
from presentation.components.comparison_table import show_comparison_table
|
| 54 |
pid_a = gr.Textbox(label="Portfolio A", value="3852a354-e66e-4bc5-97e9-55124e31e687")
|
| 55 |
pid_b = gr.Textbox(label="Portfolio B", value="b1ef37aa-5b9a-41b4-8823f2de36bb")
|
| 56 |
compare_btn = gr.Button("Load Comparison", variant="primary")
|
application/__init__.py
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Application layer services orchestrating domain and infrastructure."""
|
| 2 |
+
|
| 3 |
+
from .chat_assistant import ChatAssistant
|
| 4 |
+
from .metrics_table import show_metrics_table
|
| 5 |
+
from .portfolio_analyzer import PortfolioAnalyzer
|
| 6 |
+
from .portfolio_comparer import PortfolioComparer
|
| 7 |
+
|
| 8 |
+
__all__ = [
|
| 9 |
+
"ChatAssistant",
|
| 10 |
+
"PortfolioAnalyzer",
|
| 11 |
+
"PortfolioComparer",
|
| 12 |
+
"show_metrics_table",
|
| 13 |
+
]
|
core/chat.py → application/chat_assistant.py
RENAMED
|
@@ -7,7 +7,8 @@ Purpose: General chat interface for user questions about investments or portfoli
|
|
| 7 |
"""
|
| 8 |
|
| 9 |
from typing import Generator
|
| 10 |
-
|
|
|
|
| 11 |
from prompts.system_prompts import GENERAL_CONTEXT
|
| 12 |
|
| 13 |
|
|
|
|
| 7 |
"""
|
| 8 |
|
| 9 |
from typing import Generator
|
| 10 |
+
|
| 11 |
+
from infrastructure.llm_client import llm_service
|
| 12 |
from prompts.system_prompts import GENERAL_CONTEXT
|
| 13 |
|
| 14 |
|
core/metrics.py → application/metrics_table.py
RENAMED
|
@@ -6,9 +6,11 @@ Purpose: Provides async utilities to fetch and display portfolio metrics as a Da
|
|
| 6 |
Назначение: предоставляет асинхронные функции для получения и отображения метрик портфеля в виде DataFrame.
|
| 7 |
"""
|
| 8 |
|
| 9 |
-
import pandas as pd
|
| 10 |
import asyncio
|
| 11 |
-
|
|
|
|
|
|
|
|
|
|
| 12 |
|
| 13 |
|
| 14 |
def show_metrics_table(portfolio_input: str):
|
|
|
|
| 6 |
Назначение: предоставляет асинхронные функции для получения и отображения метрик портфеля в виде DataFrame.
|
| 7 |
"""
|
| 8 |
|
|
|
|
| 9 |
import asyncio
|
| 10 |
+
|
| 11 |
+
import pandas as pd
|
| 12 |
+
|
| 13 |
+
from infrastructure.output_api import extract_portfolio_id, fetch_metrics_async
|
| 14 |
|
| 15 |
|
| 16 |
def show_metrics_table(portfolio_input: str):
|
core/analyzer.py → application/portfolio_analyzer.py
RENAMED
|
@@ -8,8 +8,9 @@ Purpose: Handles single-portfolio analysis using LLM. Fetches metrics, builds pr
|
|
| 8 |
|
| 9 |
import asyncio
|
| 10 |
from typing import Generator
|
| 11 |
-
|
| 12 |
-
from
|
|
|
|
| 13 |
from prompts.system_prompts import ANALYSIS_SYSTEM_PROMPT
|
| 14 |
from prompts.reference_templates import REFERENCE_PROMPT
|
| 15 |
|
|
|
|
| 8 |
|
| 9 |
import asyncio
|
| 10 |
from typing import Generator
|
| 11 |
+
|
| 12 |
+
from infrastructure.output_api import extract_portfolio_id, fetch_metrics_async
|
| 13 |
+
from infrastructure.llm_client import llm_service
|
| 14 |
from prompts.system_prompts import ANALYSIS_SYSTEM_PROMPT
|
| 15 |
from prompts.reference_templates import REFERENCE_PROMPT
|
| 16 |
|
core/comparer.py → application/portfolio_comparer.py
RENAMED
|
@@ -8,8 +8,9 @@ Purpose: Compares two portfolios using LLM. Fetches metrics for both and builds
|
|
| 8 |
|
| 9 |
import asyncio
|
| 10 |
from typing import Generator
|
| 11 |
-
|
| 12 |
-
from
|
|
|
|
| 13 |
from prompts.system_prompts import COMPARISON_SYSTEM_PROMPT
|
| 14 |
from prompts.reference_templates import REFERENCE_COMPARISON_PROMPT
|
| 15 |
|
|
|
|
| 8 |
|
| 9 |
import asyncio
|
| 10 |
from typing import Generator
|
| 11 |
+
|
| 12 |
+
from infrastructure.output_api import extract_portfolio_id, fetch_metrics_async
|
| 13 |
+
from infrastructure.llm_client import llm_service
|
| 14 |
from prompts.system_prompts import COMPARISON_SYSTEM_PROMPT
|
| 15 |
from prompts.reference_templates import REFERENCE_COMPARISON_PROMPT
|
| 16 |
|
core/__init__.py
CHANGED
|
@@ -1,2 +1,13 @@
|
|
| 1 |
-
|
| 2 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Legacy compatibility layer bridging old imports to the new structure."""
|
| 2 |
+
|
| 3 |
+
from application.chat_assistant import ChatAssistant
|
| 4 |
+
from application.metrics_table import show_metrics_table
|
| 5 |
+
from application.portfolio_analyzer import PortfolioAnalyzer
|
| 6 |
+
from application.portfolio_comparer import PortfolioComparer
|
| 7 |
+
|
| 8 |
+
__all__ = [
|
| 9 |
+
"ChatAssistant",
|
| 10 |
+
"PortfolioAnalyzer",
|
| 11 |
+
"PortfolioComparer",
|
| 12 |
+
"show_metrics_table",
|
| 13 |
+
]
|
domain/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Domain layer placeholder for future data models in the prototype."""
|
| 2 |
+
|
| 3 |
+
__all__: list[str] = []
|
infrastructure/__init__.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Infrastructure adapters for external services and data providers."""
|
| 2 |
+
|
| 3 |
+
from . import market_data
|
| 4 |
+
from .llm_client import FeatherlessLLM, llm_service
|
| 5 |
+
from .output_api import (
|
| 6 |
+
extract_portfolio_id,
|
| 7 |
+
fetch_absolute_pnl_async,
|
| 8 |
+
fetch_metrics_async,
|
| 9 |
+
)
|
| 10 |
+
|
| 11 |
+
__all__ = [
|
| 12 |
+
"FeatherlessLLM",
|
| 13 |
+
"llm_service",
|
| 14 |
+
"extract_portfolio_id",
|
| 15 |
+
"fetch_absolute_pnl_async",
|
| 16 |
+
"fetch_metrics_async",
|
| 17 |
+
"market_data",
|
| 18 |
+
]
|
{services → infrastructure}/llm_client.py
RENAMED
|
File without changes
|
infrastructure/market_data/__init__.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Market data providers used across the prototype."""
|
| 2 |
+
|
| 3 |
+
from . import binance, coinlore, yfinance
|
| 4 |
+
|
| 5 |
+
__all__ = ["binance", "coinlore", "yfinance"]
|
core/data_binance.py → infrastructure/market_data/binance.py
RENAMED
|
File without changes
|
core/data_coinlore.py → infrastructure/market_data/coinlore.py
RENAMED
|
File without changes
|
core/data_yfinance.py → infrastructure/market_data/yfinance.py
RENAMED
|
File without changes
|
{services → infrastructure}/output_api.py
RENAMED
|
File without changes
|
presentation/__init__.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Presentation layer: UI components, charts, and styles."""
|
| 2 |
+
|
| 3 |
+
from . import components, styles
|
| 4 |
+
|
| 5 |
+
__all__ = ["components", "styles"]
|
presentation/components/__init__.py
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Individual UI components used by the Gradio interface."""
|
| 2 |
+
|
| 3 |
+
from .comparison_table import show_comparison_table
|
| 4 |
+
from .crypto_dashboard import build_crypto_dashboard
|
| 5 |
+
from .visual_comparison import build_price_chart, build_volatility_chart
|
| 6 |
+
|
| 7 |
+
__all__ = [
|
| 8 |
+
"show_comparison_table",
|
| 9 |
+
"build_crypto_dashboard",
|
| 10 |
+
"build_price_chart",
|
| 11 |
+
"build_volatility_chart",
|
| 12 |
+
]
|
{core → presentation/components}/comparison_table.py
RENAMED
|
@@ -6,10 +6,12 @@ Purpose: Generates comparative DataFrame for two portfolios and an LLM commentar
|
|
| 6 |
Назначение: создаёт сравнительную таблицу метрик двух портфелей и комментарий LLM.
|
| 7 |
"""
|
| 8 |
|
| 9 |
-
import pandas as pd
|
| 10 |
import asyncio
|
| 11 |
-
|
| 12 |
-
|
|
|
|
|
|
|
|
|
|
| 13 |
from prompts.system_prompts import COMPARISON_SYSTEM_PROMPT
|
| 14 |
|
| 15 |
|
|
|
|
| 6 |
Назначение: создаёт сравнительную таблицу метрик двух портфелей и комментарий LLM.
|
| 7 |
"""
|
| 8 |
|
|
|
|
| 9 |
import asyncio
|
| 10 |
+
|
| 11 |
+
import pandas as pd
|
| 12 |
+
|
| 13 |
+
from infrastructure.llm_client import llm_service
|
| 14 |
+
from infrastructure.output_api import extract_portfolio_id, fetch_metrics_async
|
| 15 |
from prompts.system_prompts import COMPARISON_SYSTEM_PROMPT
|
| 16 |
|
| 17 |
|
{core → presentation/components}/crypto_dashboard.py
RENAMED
|
@@ -5,9 +5,11 @@ Crypto Dashboard — Plotly Edition (clean layout)
|
|
| 5 |
• без глобального Markdown-заголовка
|
| 6 |
"""
|
| 7 |
import requests
|
|
|
|
| 8 |
import pandas as pd
|
| 9 |
import plotly.express as px
|
| 10 |
-
|
|
|
|
| 11 |
|
| 12 |
|
| 13 |
def fetch_coinlore_data(limit=100):
|
|
|
|
| 5 |
• без глобального Markdown-заголовка
|
| 6 |
"""
|
| 7 |
import requests
|
| 8 |
+
|
| 9 |
import pandas as pd
|
| 10 |
import plotly.express as px
|
| 11 |
+
|
| 12 |
+
from infrastructure.llm_client import llm_service
|
| 13 |
|
| 14 |
|
| 15 |
def fetch_coinlore_data(limit=100):
|
{core → presentation/components}/multi_charts.py
RENAMED
|
File without changes
|
{core → presentation/components}/visual_comparison.py
RENAMED
|
File without changes
|
{core → presentation/components}/visualization.py
RENAMED
|
File without changes
|
presentation/styles/__init__.py
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Static style assets for the presentation layer."""
|
| 2 |
+
|
| 3 |
+
__all__ = [
|
| 4 |
+
"themes",
|
| 5 |
+
]
|
presentation/styles/themes/__init__.py
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Theme CSS assets for the presentation layer."""
|
| 2 |
+
|
| 3 |
+
__all__: list[str] = []
|
{core/styles → presentation/styles/themes}/base.css
RENAMED
|
File without changes
|
{core/styles → presentation/styles/themes}/crypto_dashboard.css
RENAMED
|
File without changes
|
{core/styles → presentation/styles/themes}/multi_charts.css
RENAMED
|
File without changes
|
{core → presentation/styles}/ui_style.css
RENAMED
|
File without changes
|
services/__init__.py
DELETED
|
@@ -1,2 +0,0 @@
|
|
| 1 |
-
# __init__.py
|
| 2 |
-
# Marks this directory as a Python package.
|
|
|
|
|
|
|
|
|