QAway-to commited on
Commit
0d9474a
·
1 Parent(s): 135a260

Refactor project into layered prototype structure

Browse files
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
- from services.llm_client import llm_service
3
- from core.analyzer import PortfolioAnalyzer
4
- from core.comparer import PortfolioComparer
5
- from core.chat import ChatAssistant
6
- from core.metrics import show_metrics_table
7
- from core.crypto_dashboard import build_crypto_dashboard # Plotly dashboard + KPI-line
8
- from core.visual_comparison import build_price_chart, build_volatility_chart # Interactive pair comparison
 
 
 
 
 
 
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 = load_css("core/styles/base.css")
17
- crypto_css = load_css("core/styles/crypto_dashboard.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 core.comparison_table import show_comparison_table
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
- from services.llm_client import llm_service
 
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
- from services.output_api import extract_portfolio_id, fetch_metrics_async
 
 
 
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
- from services.output_api import extract_portfolio_id, fetch_metrics_async
12
- from services.llm_client import llm_service
 
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
- from services.output_api import extract_portfolio_id, fetch_metrics_async
12
- from services.llm_client import llm_service
 
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
- # __init__.py
2
- # Marks this directory as a Python package.
 
 
 
 
 
 
 
 
 
 
 
 
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
- from services.output_api import fetch_metrics_async, extract_portfolio_id
12
- from services.llm_client import llm_service
 
 
 
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
- from services.llm_client import llm_service
 
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.