Ready for first real test
This commit is contained in:
1 parent
acddf9135f
commit
b91c81ebdf
46 files changed
+34903
-26
No files matched your search
+11
-2
@@ -27,9 +27,18 @@ LLM_MAX_TOKENS=2048
|
|||||||
|
|
||||||
# ---- Per-agent budgets ----
|
# ---- Per-agent budgets ----
|
||||||
PER_AGENT_WALLCLOCK_MIN=130
|
PER_AGENT_WALLCLOCK_MIN=130
|
||||||
PER_AGENT_STEP_BUDGET=60
|
PER_AGENT_MIN_MINUTES=120 # TIME FLOOR: research at least this long (~2hr). Lower to test.
|
||||||
PER_AGENT_MIN_STEPS=25 # keep researching (nudge) until at least this many steps — deeper research
|
PER_AGENT_STEP_BUDGET=400 # high safety cap (don't cap before the time floor)
|
||||||
|
PER_AGENT_MIN_STEPS=25
|
||||||
HARD_STOP=07:00
|
HARD_STOP=07:00
|
||||||
|
SANDBOX_TIMEOUT_S=90 # run_analysis script wall-clock limit
|
||||||
|
|
||||||
|
# ---- Email daily report (Gmail App Password — NOT your real password; blank = skip) ----
|
||||||
|
EMAIL_TO=
|
||||||
|
EMAIL_FROM=
|
||||||
|
SMTP_HOST=smtp.gmail.com
|
||||||
|
SMTP_PORT=587
|
||||||
|
EMAIL_APP_PASSWORD=
|
||||||
|
|
||||||
# ---- Timezones (market clock is authoritative; local is your Mac) ----
|
# ---- Timezones (market clock is authoritative; local is your Mac) ----
|
||||||
MARKET_TZ=America/New_York
|
MARKET_TZ=America/New_York
|
||||||
|
|||||||
+31
-11
@@ -41,11 +41,22 @@ RESEARCH_KICKOFF = (
|
|||||||
"call get_option_chain(underlying, 'call' or 'put') and copy an exact contract_symbol."
|
"call get_option_chain(underlying, 'call' or 'put') and copy an exact contract_symbol."
|
||||||
)
|
)
|
||||||
|
|
||||||
DEEPEN_NUDGE = (
|
# Rotating directive nudges — cycled so a long (~2hr) research session stays PRODUCTIVE
|
||||||
"You're not finished. Keep researching before finalizing: evaluate additional candidate "
|
# (does new, varied work) instead of looping "am I done?". Keep using tools.
|
||||||
"tickers you haven't examined yet, pull bars/news/fundamentals for them, verify with "
|
DEEPEN_NUDGES = [
|
||||||
"web_search, and stress-test your leading ideas against the alternatives. Continue using tools."
|
"Not done yet. Screen 5-10 MORE candidate tickers in your style you haven't examined; pull "
|
||||||
)
|
"get_bars / get_news / get_fundamentals on them.",
|
||||||
|
"Build the BEAR case for your current top pick: what would make it wrong? Find data that "
|
||||||
|
"challenges your thesis (web_search + web_fetch recent coverage).",
|
||||||
|
"Use run_analysis to write a real script: fetch prices with yfinance, compute momentum / "
|
||||||
|
"moving averages / volatility / correlations, and save a chart to back your best idea.",
|
||||||
|
"Zoom out: check the macro + sector context (rates, the sector ETF, peers) via web_search and "
|
||||||
|
"get_bars. Does it support or undercut your picks?",
|
||||||
|
"Compare your leading picks head-to-head against 2-3 sector peers on valuation and momentum "
|
||||||
|
"(get_fundamentals + run_analysis). Are you picking the best expression of the thesis?",
|
||||||
|
"Re-examine catalysts and timing for each candidate (earnings dates, events via get_news / "
|
||||||
|
"web_search) and refine entry, stop, and take-profit levels with run_python.",
|
||||||
|
]
|
||||||
|
|
||||||
FINALIZE_PROMPT = (
|
FINALIZE_PROMPT = (
|
||||||
"Research complete. Return a JSON object of the form {{\"picks\": [ ... ]}} containing about "
|
"Research complete. Return a JSON object of the form {{\"picks\": [ ... ]}} containing about "
|
||||||
@@ -176,6 +187,7 @@ def run_agent(
|
|||||||
deadline_ts: float,
|
deadline_ts: float,
|
||||||
step_budget: int,
|
step_budget: int,
|
||||||
min_steps: int = 0,
|
min_steps: int = 0,
|
||||||
|
min_seconds: float = 0.0,
|
||||||
checkpointer=None,
|
checkpointer=None,
|
||||||
model_name: str = "",
|
model_name: str = "",
|
||||||
) -> AgentOutput:
|
) -> AgentOutput:
|
||||||
@@ -195,6 +207,7 @@ def run_agent(
|
|||||||
running_summary = ""
|
running_summary = ""
|
||||||
reasoning_trace: list[str] = []
|
reasoning_trace: list[str] = []
|
||||||
steps_used = 0
|
steps_used = 0
|
||||||
|
nudge_count = 0
|
||||||
ended_reason = "natural"
|
ended_reason = "natural"
|
||||||
|
|
||||||
def snapshot(status: AgentStatus, picks: list[Pick] | None = None, errors=None) -> AgentOutput:
|
def snapshot(status: AgentStatus, picks: list[Pick] | None = None, errors=None) -> AgentOutput:
|
||||||
@@ -229,11 +242,16 @@ def run_agent(
|
|||||||
|
|
||||||
tool_calls = getattr(ai, "tool_calls", None)
|
tool_calls = getattr(ai, "tool_calls", None)
|
||||||
if not tool_calls:
|
if not tool_calls:
|
||||||
# Force depth: if the model wants to stop early, nudge it to keep researching
|
# Force depth: if the model tries to finalize before the TIME floor (or step floor),
|
||||||
# until it has done at least min_steps (still bounded by the wall-clock deadline).
|
# inject a rotating directive nudge so it keeps doing NEW research. Bounded by deadline.
|
||||||
if steps_used < min_steps and time.time() < deadline_ts:
|
elapsed = time.time() - start
|
||||||
messages.append(HumanMessage(content=DEEPEN_NUDGE))
|
if (elapsed < min_seconds or steps_used < min_steps) and time.time() < deadline_ts:
|
||||||
reasoning_trace.append(f"[nudge] deepen research ({steps_used}/{min_steps} steps)")
|
nudge = DEEPEN_NUDGES[nudge_count % len(DEEPEN_NUDGES)]
|
||||||
|
nudge_count += 1
|
||||||
|
messages.append(HumanMessage(content=nudge))
|
||||||
|
reasoning_trace.append(
|
||||||
|
f"[nudge #{nudge_count}] {elapsed/60:.0f}/{min_seconds/60:.0f}min elapsed, step {steps_used}"
|
||||||
|
)
|
||||||
continue
|
continue
|
||||||
if ai.content:
|
if ai.content:
|
||||||
reasoning_trace.append(f"[note] {str(ai.content)[:200]}")
|
reasoning_trace.append(f"[note] {str(ai.content)[:200]}")
|
||||||
@@ -241,7 +259,9 @@ def run_agent(
|
|||||||
break
|
break
|
||||||
|
|
||||||
for tc in tool_calls:
|
for tc in tool_calls:
|
||||||
name, args = tc["name"], tc.get("args", {}) or {}
|
# gpt-oss can leak harmony channel tokens into the tool name (e.g. "get_bars<|channel|>")
|
||||||
|
name = tc["name"].split("<|")[0].strip()
|
||||||
|
args = tc.get("args", {}) or {}
|
||||||
tool = tools_by_name.get(name)
|
tool = tools_by_name.get(name)
|
||||||
try:
|
try:
|
||||||
result = tool.invoke(args) if tool else f"ERROR: unknown tool {name}"
|
result = tool.invoke(args) if tool else f"ERROR: unknown tool {name}"
|
||||||
|
|||||||
+22
-5
@@ -50,10 +50,19 @@ class Settings(BaseSettings):
|
|||||||
llm_max_tokens: int = 2048
|
llm_max_tokens: int = 2048
|
||||||
|
|
||||||
# --- Budgets ---
|
# --- Budgets ---
|
||||||
per_agent_wallclock_min: int = 130
|
per_agent_wallclock_min: int = 130 # hard wall-clock cap per agent
|
||||||
per_agent_step_budget: int = 60
|
per_agent_min_minutes: int = 120 # TIME FLOOR: keep researching until at least this long
|
||||||
per_agent_min_steps: int = 25 # keep researching (nudge) until at least this many steps
|
per_agent_step_budget: int = 400 # high safety cap so steps don't cap before the time floor
|
||||||
|
per_agent_min_steps: int = 25 # secondary floor (steps)
|
||||||
hard_stop: str = "07:00" # local wall-clock HH:MM; nightly run must finish by this
|
hard_stop: str = "07:00" # local wall-clock HH:MM; nightly run must finish by this
|
||||||
|
sandbox_timeout_s: int = 90 # run_analysis script wall-clock limit
|
||||||
|
|
||||||
|
# --- Email (daily report; use a Gmail App Password, never your real password) ---
|
||||||
|
email_to: str = ""
|
||||||
|
email_from: str = ""
|
||||||
|
smtp_host: str = "smtp.gmail.com"
|
||||||
|
smtp_port: int = 587
|
||||||
|
email_app_password: str = ""
|
||||||
|
|
||||||
# --- Timezones ---
|
# --- Timezones ---
|
||||||
market_tz: str = "America/New_York" # authoritative market clock
|
market_tz: str = "America/New_York" # authoritative market clock
|
||||||
@@ -76,6 +85,14 @@ class Settings(BaseSettings):
|
|||||||
def db_path(self) -> Path:
|
def db_path(self) -> Path:
|
||||||
return REPO_ROOT / "state" / "trading.db"
|
return REPO_ROOT / "state" / "trading.db"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def reports_dir(self) -> Path:
|
||||||
|
return REPO_ROOT / "state" / "reports"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def sandbox_dir(self) -> Path:
|
||||||
|
return REPO_ROOT / "state" / "sandbox"
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def llm_base_url(self) -> str:
|
def llm_base_url(self) -> str:
|
||||||
return f"http://{self.llm_host}:{self.llm_port}/v1"
|
return f"http://{self.llm_host}:{self.llm_port}/v1"
|
||||||
@@ -128,8 +145,8 @@ class Settings(BaseSettings):
|
|||||||
return self.alpaca_api_key, self.alpaca_secret_key
|
return self.alpaca_api_key, self.alpaca_secret_key
|
||||||
|
|
||||||
def ensure_dirs(self) -> None:
|
def ensure_dirs(self) -> None:
|
||||||
self.output_dir.mkdir(parents=True, exist_ok=True)
|
for d in (self.output_dir, self.log_dir, self.reports_dir, self.sandbox_dir):
|
||||||
self.log_dir.mkdir(parents=True, exist_ok=True)
|
d.mkdir(parents=True, exist_ok=True)
|
||||||
|
|
||||||
|
|
||||||
@lru_cache
|
@lru_cache
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ from execution import broker as broker_mod # noqa: E402
|
|||||||
from execution import reconcile as reconcile_mod # noqa: E402
|
from execution import reconcile as reconcile_mod # noqa: E402
|
||||||
from execution.guardrails import plan_orders # noqa: E402
|
from execution.guardrails import plan_orders # noqa: E402
|
||||||
from orchestration.budget import next_market_trade_date # noqa: E402
|
from orchestration.budget import next_market_trade_date # noqa: E402
|
||||||
|
from reporting import email_report, report_writer # noqa: E402
|
||||||
from state import db # noqa: E402
|
from state import db # noqa: E402
|
||||||
from tools.market_data import latest_price # noqa: E402
|
from tools.market_data import latest_price # noqa: E402
|
||||||
from tools.options import option_premium # noqa: E402
|
from tools.options import option_premium # noqa: E402
|
||||||
@@ -168,6 +169,34 @@ def main(force: bool = False, date: str | None = None) -> int:
|
|||||||
|
|
||||||
con.close()
|
con.close()
|
||||||
logger.info("=== execution complete: {} orders placed across accounts ===", total_placed)
|
logger.info("=== execution complete: {} orders placed across accounts ===", total_placed)
|
||||||
|
|
||||||
|
# daily report: append execution results, render PDF, email it
|
||||||
|
try:
|
||||||
|
con2 = db.connect(s.db_path)
|
||||||
|
rows = con2.execute(
|
||||||
|
"SELECT agent, ticker, side, qty, status FROM orders WHERE trade_date=? "
|
||||||
|
"ORDER BY agent, ticker",
|
||||||
|
(trade_date,),
|
||||||
|
).fetchall()
|
||||||
|
lines = [f"**{total_placed} orders placed** across accounts for {trade_date}.", ""]
|
||||||
|
for persona in PERSONAS:
|
||||||
|
ords = [r for r in rows if r["agent"] == persona.id]
|
||||||
|
snap = db.latest_snapshot(con2, persona.id)
|
||||||
|
eq = f"${snap['equity']:,.2f}" if snap else "n/a"
|
||||||
|
detail = ", ".join(
|
||||||
|
f"{r['side']} {r['qty']} {r['ticker']} [{r['status']}]" for r in ords
|
||||||
|
) or "no orders"
|
||||||
|
lines.append(f"- **{persona.id}** (equity {eq}): {detail}")
|
||||||
|
con2.close()
|
||||||
|
exec_summary = "\n".join(lines)
|
||||||
|
daily = report_writer.write_daily_report(trade_date, outputs, exec_summary=exec_summary)
|
||||||
|
pdf = report_writer.to_pdf(daily)
|
||||||
|
email_report.send_daily(
|
||||||
|
trade_date, pdf, summary=f"{total_placed} orders placed for {trade_date}."
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("daily report/email step failed: {}", e)
|
||||||
|
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,7 @@ from orchestration.budget import ( # noqa: E402
|
|||||||
next_market_trade_date,
|
next_market_trade_date,
|
||||||
)
|
)
|
||||||
from orchestration.checkpoint import Checkpointer # noqa: E402
|
from orchestration.checkpoint import Checkpointer # noqa: E402
|
||||||
|
from reporting import report_writer # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
def main(compressed: bool = False) -> int:
|
def main(compressed: bool = False) -> int:
|
||||||
@@ -45,6 +46,15 @@ def main(compressed: bool = False) -> int:
|
|||||||
logger.error("oMLX not ready; aborting.")
|
logger.error("oMLX not ready; aborting.")
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
# preflight: confirm internet before committing to a long run (warn but continue)
|
||||||
|
try:
|
||||||
|
from tools.web import web_search
|
||||||
|
|
||||||
|
probe = web_search.invoke({"query": "stock market news today"})
|
||||||
|
logger.info("internet preflight: {}", "OK" if "http" in probe else "no results (degraded)")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("internet preflight failed (continuing): {}", e)
|
||||||
|
|
||||||
model_name = resolve_model_id()
|
model_name = resolve_model_id()
|
||||||
run_id = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
|
run_id = datetime.now(timezone.utc).strftime("%Y%m%dT%H%M%SZ")
|
||||||
cp = Checkpointer(s.output_dir, trade_date)
|
cp = Checkpointer(s.output_dir, trade_date)
|
||||||
@@ -61,7 +71,9 @@ def main(compressed: bool = False) -> int:
|
|||||||
per_agent_cap_s = 5 * 60 if compressed else s.per_agent_wallclock_min * 60
|
per_agent_cap_s = 5 * 60 if compressed else s.per_agent_wallclock_min * 60
|
||||||
step_budget = 12 if compressed else s.per_agent_step_budget
|
step_budget = 12 if compressed else s.per_agent_step_budget
|
||||||
min_steps = 4 if compressed else s.per_agent_min_steps
|
min_steps = 4 if compressed else s.per_agent_min_steps
|
||||||
|
min_seconds = 60 if compressed else s.per_agent_min_minutes * 60
|
||||||
|
|
||||||
|
outputs = []
|
||||||
for i, persona in enumerate(PERSONAS):
|
for i, persona in enumerate(PERSONAS):
|
||||||
now = time.time()
|
now = time.time()
|
||||||
if now >= hard_stop:
|
if now >= hard_stop:
|
||||||
@@ -71,9 +83,22 @@ def main(compressed: bool = False) -> int:
|
|||||||
logger.info("--- {} until {} ---", persona.id,
|
logger.info("--- {} until {} ---", persona.id,
|
||||||
datetime.fromtimestamp(deadline).strftime("%H:%M:%S"))
|
datetime.fromtimestamp(deadline).strftime("%H:%M:%S"))
|
||||||
out = run_agent(persona, deadline_ts=deadline, step_budget=step_budget,
|
out = run_agent(persona, deadline_ts=deadline, step_budget=step_budget,
|
||||||
min_steps=min_steps, checkpointer=cp, model_name=model_name)
|
min_steps=min_steps, min_seconds=min_seconds,
|
||||||
|
checkpointer=cp, model_name=model_name)
|
||||||
cp.write(out)
|
cp.write(out)
|
||||||
|
outputs.append(out)
|
||||||
manifest.agents.append(persona.id)
|
manifest.agents.append(persona.id)
|
||||||
|
try:
|
||||||
|
report_writer.write_agent_report(out, trade_date) # analyst research note
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("agent report failed for {}: {}", persona.id, e)
|
||||||
|
|
||||||
|
# combined daily report (execution results appended in the morning by run_market_open)
|
||||||
|
try:
|
||||||
|
daily = report_writer.write_daily_report(trade_date, outputs)
|
||||||
|
report_writer.to_pdf(daily)
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("daily report failed: {}", e)
|
||||||
|
|
||||||
manifest.status = "complete"
|
manifest.status = "complete"
|
||||||
manifest.ended_at = datetime.now(timezone.utc)
|
manifest.ended_at = datetime.now(timezone.utc)
|
||||||
|
|||||||
@@ -22,6 +22,10 @@ dependencies = [
|
|||||||
"pandas",
|
"pandas",
|
||||||
"streamlit",
|
"streamlit",
|
||||||
"loguru",
|
"loguru",
|
||||||
|
"matplotlib>=3.11.0",
|
||||||
|
"scipy>=1.17.1",
|
||||||
|
"markdown>=3.10.2",
|
||||||
|
"xhtml2pdf>=0.2.17",
|
||||||
]
|
]
|
||||||
|
|
||||||
[dependency-groups]
|
[dependency-groups]
|
||||||
|
|||||||
@@ -0,0 +1,47 @@
|
|||||||
|
"""Email the daily report via SMTP (Gmail app password). No-op if EMAIL_* is unconfigured,
|
||||||
|
so the pipeline never fails just because email isn't set up.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import smtplib
|
||||||
|
import ssl
|
||||||
|
from email.message import EmailMessage
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
from config.settings import get_settings
|
||||||
|
|
||||||
|
|
||||||
|
def send_daily(trade_date: str, pdf_path: Path | None, summary: str = "") -> bool:
|
||||||
|
s = get_settings()
|
||||||
|
if not (s.email_app_password and s.email_to and s.email_from):
|
||||||
|
logger.info("email skipped (EMAIL_TO/FROM/APP_PASSWORD not set in .env)")
|
||||||
|
return False
|
||||||
|
|
||||||
|
msg = EmailMessage()
|
||||||
|
msg["Subject"] = f"Trading Agent — Daily Report {trade_date}"
|
||||||
|
msg["From"] = s.email_from
|
||||||
|
msg["To"] = s.email_to
|
||||||
|
msg.set_content(summary or f"Daily research + execution report for {trade_date} attached.")
|
||||||
|
|
||||||
|
if pdf_path and Path(pdf_path).exists():
|
||||||
|
msg.add_attachment(
|
||||||
|
Path(pdf_path).read_bytes(),
|
||||||
|
maintype="application",
|
||||||
|
subtype="pdf",
|
||||||
|
filename=Path(pdf_path).name,
|
||||||
|
)
|
||||||
|
|
||||||
|
try:
|
||||||
|
ctx = ssl.create_default_context()
|
||||||
|
with smtplib.SMTP(s.smtp_host, s.smtp_port, timeout=30) as server:
|
||||||
|
server.starttls(context=ctx)
|
||||||
|
server.login(s.email_from, s.email_app_password)
|
||||||
|
server.send_message(msg)
|
||||||
|
logger.info("emailed daily report to {}", s.email_to)
|
||||||
|
return True
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("email failed: {}", e)
|
||||||
|
return False
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
"""Analyst report generation.
|
||||||
|
|
||||||
|
Turns an agent's picks + research trace into a professional Markdown research note (with any
|
||||||
|
charts the agent generated via run_analysis embedded), a combined daily report, and PDF
|
||||||
|
exports. Stored under state/reports/<trade_date>/.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import re
|
||||||
|
import shutil
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from loguru import logger
|
||||||
|
|
||||||
|
from langchain_core.messages import HumanMessage, SystemMessage
|
||||||
|
|
||||||
|
from agents.schemas import AgentOutput
|
||||||
|
from config.settings import get_settings
|
||||||
|
from llm.client import build_llm
|
||||||
|
|
||||||
|
_CHART_RE = re.compile(r"([^\s,]+\.png)")
|
||||||
|
|
||||||
|
REPORT_SYSTEM = (
|
||||||
|
"You are a senior sell-side equity research analyst. Write a clear, professional research "
|
||||||
|
"note in GitHub-flavored Markdown. Be specific and data-driven; cite the tickers, prices, and "
|
||||||
|
"numbers from the research. Do not invent data you weren't given. Output ONLY Markdown."
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _reports_day_dir(trade_date: str) -> Path:
|
||||||
|
d = get_settings().reports_dir / trade_date
|
||||||
|
d.mkdir(parents=True, exist_ok=True)
|
||||||
|
return d
|
||||||
|
|
||||||
|
|
||||||
|
def _collect_charts(out: AgentOutput, dest_dir: Path) -> list[str]:
|
||||||
|
"""Find chart PNGs the agent made via run_analysis (logged in reasoning_trace), copy them
|
||||||
|
next to the report, and return the local filenames to embed."""
|
||||||
|
names: list[str] = []
|
||||||
|
seen: set[str] = set()
|
||||||
|
for line in out.reasoning_trace:
|
||||||
|
if "Artifacts written" not in line:
|
||||||
|
continue
|
||||||
|
for m in _CHART_RE.findall(line):
|
||||||
|
src = Path(m)
|
||||||
|
if src.suffix.lower() == ".png" and src.exists() and str(src) not in seen:
|
||||||
|
seen.add(str(src))
|
||||||
|
fname = f"{out.persona_id}_{len(names) + 1}.png"
|
||||||
|
try:
|
||||||
|
shutil.copyfile(src, dest_dir / fname)
|
||||||
|
names.append(fname)
|
||||||
|
except Exception:
|
||||||
|
pass
|
||||||
|
return names
|
||||||
|
|
||||||
|
|
||||||
|
def write_agent_report(out: AgentOutput, trade_date: str) -> Path:
|
||||||
|
"""Generate + save one agent's Markdown research note. Returns the .md path."""
|
||||||
|
day_dir = _reports_day_dir(trade_date)
|
||||||
|
charts = _collect_charts(out, day_dir)
|
||||||
|
picks_json = "\n".join(
|
||||||
|
f"- {p.ticker} [{p.instrument.value}/{p.direction.value}] conv={p.conviction} "
|
||||||
|
f"weight={p.requested_weight_pct} stop={p.stop_loss_pct} take={p.take_profit_pct} :: {p.thesis}"
|
||||||
|
for p in out.picks
|
||||||
|
) or "(no picks)"
|
||||||
|
findings = "\n".join(out.reasoning_trace[-60:])
|
||||||
|
chart_note = (
|
||||||
|
"Embed these charts where relevant using Markdown image syntax "
|
||||||
|
f"(e.g. ): {', '.join(charts)}" if charts else "No charts were generated."
|
||||||
|
)
|
||||||
|
human = (
|
||||||
|
f"Trade date: {trade_date}. Analyst persona: {out.agent_name}.\n"
|
||||||
|
f"Status: {out.status.value}. Market summary: {out.market_summary or '(none)'}\n\n"
|
||||||
|
f"FINAL PICKS:\n{picks_json}\n\n"
|
||||||
|
f"RESEARCH TRACE (tool calls + findings):\n{findings}\n\n"
|
||||||
|
f"{chart_note}\n\n"
|
||||||
|
"Write the note with these sections:\n"
|
||||||
|
"# <Analyst name> — Research Note (<date>)\n"
|
||||||
|
"## Market Overview (what regime/backdrop framed today's work)\n"
|
||||||
|
"## Picks — one `###` per pick: direction+conviction, thesis, supporting data/valuation, "
|
||||||
|
"catalysts, key risks, and the entry/stop/take plan.\n"
|
||||||
|
"## Portfolio Notes (how these fit existing holdings)\n"
|
||||||
|
"## Summary Table (Markdown table: Ticker | Dir | Conv | Weight | Stop | Take | One-liner)"
|
||||||
|
)
|
||||||
|
try:
|
||||||
|
resp = build_llm(max_tokens=4096).invoke(
|
||||||
|
[SystemMessage(content=REPORT_SYSTEM), HumanMessage(content=human)]
|
||||||
|
)
|
||||||
|
md = resp.content if isinstance(resp.content, str) else str(resp.content)
|
||||||
|
if not md.strip():
|
||||||
|
raise ValueError("empty report content")
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("report gen failed for {}: {}", out.persona_id, e)
|
||||||
|
md = f"# {out.agent_name} — Research Note ({trade_date})\n\n(Report generation failed: {e})\n\n{picks_json}"
|
||||||
|
|
||||||
|
md_path = day_dir / f"{out.persona_id}.md"
|
||||||
|
md_path.write_text(md)
|
||||||
|
logger.info("wrote report {}", md_path)
|
||||||
|
return md_path
|
||||||
|
|
||||||
|
|
||||||
|
def write_daily_report(trade_date: str, outputs: list[AgentOutput], exec_summary: str = "") -> Path:
|
||||||
|
"""Combine the agents into one daily report (links their notes + a portfolio-wide summary)."""
|
||||||
|
day_dir = _reports_day_dir(trade_date)
|
||||||
|
lines = [f"# Daily Research — {trade_date}\n"]
|
||||||
|
if exec_summary:
|
||||||
|
lines += ["## Execution Results\n", exec_summary, ""]
|
||||||
|
lines.append("## Agents\n")
|
||||||
|
for out in outputs:
|
||||||
|
tickers = ", ".join(
|
||||||
|
f"{p.ticker}({p.direction.value},{p.conviction:.2f})" for p in out.picks
|
||||||
|
) or "no picks"
|
||||||
|
lines.append(f"### {out.agent_name} — *{out.status.value}*")
|
||||||
|
lines.append(f"- Picks: {tickers}")
|
||||||
|
lines.append(f"- Full note: [`{out.persona_id}.md`]({out.persona_id}.md)\n")
|
||||||
|
path = day_dir / "daily.md"
|
||||||
|
path.write_text("\n".join(lines))
|
||||||
|
logger.info("wrote daily report {}", path)
|
||||||
|
return path
|
||||||
|
|
||||||
|
|
||||||
|
def to_pdf(md_path: Path) -> Path | None:
|
||||||
|
"""Render a Markdown report to PDF (embeds relative-path charts). None on failure."""
|
||||||
|
try:
|
||||||
|
import markdown as md_lib
|
||||||
|
from xhtml2pdf import pisa
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("PDF deps missing: {}", e)
|
||||||
|
return None
|
||||||
|
md_path = Path(md_path)
|
||||||
|
base = md_path.parent
|
||||||
|
html_body = md_lib.markdown(md_path.read_text(), extensions=["tables", "fenced_code"])
|
||||||
|
html = (
|
||||||
|
"<html><head><style>body{font-family:Helvetica,Arial,sans-serif;font-size:11px;} "
|
||||||
|
"h1{font-size:20px;} h2{font-size:15px;border-bottom:1px solid #ccc;} h3{font-size:13px;} "
|
||||||
|
"table{border-collapse:collapse;} td,th{border:1px solid #999;padding:3px;} "
|
||||||
|
f"img{{max-width:520px;}}</style></head><body>{html_body}</body></html>"
|
||||||
|
)
|
||||||
|
|
||||||
|
def _link_cb(uri: str, rel: str) -> str:
|
||||||
|
p = base / uri
|
||||||
|
return str(p) if p.exists() else uri
|
||||||
|
|
||||||
|
pdf_path = md_path.with_suffix(".pdf")
|
||||||
|
try:
|
||||||
|
with open(pdf_path, "wb") as f:
|
||||||
|
pisa.CreatePDF(html, dest=f, link_callback=_link_cb)
|
||||||
|
return pdf_path
|
||||||
|
except Exception as e:
|
||||||
|
logger.warning("PDF render failed for {}: {}", md_path, e)
|
||||||
|
return None
|
||||||
@@ -0,0 +1,54 @@
|
|||||||
|
"""Tool health check — exercise every agent tool and print PASS/FAIL.
|
||||||
|
|
||||||
|
Confirms internet access, market-data APIs, the sandbox, and (critically) that the sandbox
|
||||||
|
CANNOT read account secrets. Run anytime: uv run python scripts/check_tools.py
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import pathlib
|
||||||
|
import sys
|
||||||
|
|
||||||
|
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent.parent))
|
||||||
|
|
||||||
|
from tools.market_data import get_bars, get_quote # noqa: E402
|
||||||
|
from tools.options import get_option_chain # noqa: E402
|
||||||
|
from tools.python_exec import run_python # noqa: E402
|
||||||
|
from tools.sandbox import run_analysis # noqa: E402
|
||||||
|
from tools.web import web_fetch, web_search # noqa: E402
|
||||||
|
|
||||||
|
|
||||||
|
def _check(name: str, fn) -> bool:
|
||||||
|
try:
|
||||||
|
r = str(fn())
|
||||||
|
ok = not r.startswith("ERROR")
|
||||||
|
print(f"[{'PASS' if ok else 'FAIL'}] {name}: {r[:80].strip()}")
|
||||||
|
return ok
|
||||||
|
except Exception as e:
|
||||||
|
print(f"[FAIL] {name}: {type(e).__name__}: {e}")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
checks = [
|
||||||
|
("run_python (calc)", lambda: run_python.invoke({"code": "print(2 + 2)"})),
|
||||||
|
("web_search (INTERNET)", lambda: web_search.invoke({"query": "stock market news"})),
|
||||||
|
("web_fetch (INTERNET)", lambda: web_fetch.invoke({"url": "https://example.com"})),
|
||||||
|
("get_quote", lambda: get_quote.invoke({"ticker": "AAPL"})),
|
||||||
|
("get_bars", lambda: get_bars.invoke({"ticker": "AAPL"})),
|
||||||
|
("get_option_chain", lambda: get_option_chain.invoke({"underlying": "AAPL", "right": "call"})),
|
||||||
|
("run_analysis (sandbox)", lambda: run_analysis.invoke({"code": "import pandas, numpy; print('libs ok')"})),
|
||||||
|
(
|
||||||
|
"sandbox SECRET-SCRUB",
|
||||||
|
lambda: "PASS"
|
||||||
|
if "None" in run_analysis.invoke({"code": "import os; print(os.environ.get('ALPACA_NOVA_SECRET'))"})
|
||||||
|
else "ERROR: secret leaked into sandbox!",
|
||||||
|
),
|
||||||
|
]
|
||||||
|
results = [_check(n, f) for n, f in checks]
|
||||||
|
print(f"\n{sum(results)}/{len(results)} tools OK")
|
||||||
|
return 0 if all(results) else 1
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
@@ -0,0 +1,117 @@
|
|||||||
|
# Atlas — Research Note (2026-06-30)
|
||||||
|
|
||||||
|
## Market Overview
|
||||||
|
The U.S. economy is poised for a potential rate hike in the July 2026 FOMC meeting. The June 2026 Consumer Price Index (CPI) is scheduled for release on **July 14 2026** (BLS, https://www.bls.gov/cpi/), and the Federal Reserve is expected to respond to the forecasted inflation trajectory with a tightening cycle. A higher policy rate would compress net interest margins for banks but simultaneously broaden the yield curve, boosting net interest income (NII) for large, diversified institutions. Market volatility is likely to spike, providing fee‑income opportunities for investment banks that specialize in trading and advisory services.
|
||||||
|
|
||||||
|
Key catalysts for the next few weeks:
|
||||||
|
- **July 14 2026:** June CPI data release
|
||||||
|
- **July 20 2026:** FOMC policy meeting and statement
|
||||||
|
- **July 22 2026:** Fed’s quantitative tightening (QT) schedule updates
|
||||||
|
|
||||||
|
Risks include a weaker-than‑expected CPI, a dovish Fed stance, or a sudden drop in market volatility that would hurt fee income.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Picks
|
||||||
|
|
||||||
|
### JPMorgan Chase & Co. (JPM) – **Long**
|
||||||
|
- **Conviction:** 0.72
|
||||||
|
- **Weight:** 0.08
|
||||||
|
- **Stop:** 0.07 (≈ $22.77)
|
||||||
|
- **Take:** 0.15 (≈ $49.15)
|
||||||
|
|
||||||
|
**Thesis**
|
||||||
|
JPM’s diversified loan portfolio—corporate, consumer, and mortgage—positions it to capture higher NII as the Fed hikes rates. Its strong capital base (CET1 ≈ 14.8%) and robust liquidity profile (LCR > 120%) reduce balance‑sheet risk. The bank’s fee income is modestly exposed to market volatility; however, the primary driver will be the interest‑rate environment.
|
||||||
|
|
||||||
|
**Supporting Data**
|
||||||
|
- **Last Close**: **$327.33** (yfinance)
|
||||||
|
- **30‑Day Performance**: +8.85 % (high $343.45, low $293.67)
|
||||||
|
- **Average Volume**: 9 469 553 shares
|
||||||
|
- **Recent 5‑Day Closes**: 333.45, 335.12, 329.05, 329.39, 327.33
|
||||||
|
|
||||||
|
**Catalysts**
|
||||||
|
- **July 14 CPI**: If inflation remains above the Fed’s 2% target, the FOMC is likely to raise rates.
|
||||||
|
- **July 20 FOMC**: Confirmation of tightening will lift the forward rate curve and JPM’s NII projection.
|
||||||
|
|
||||||
|
**Key Risks**
|
||||||
|
- Fed keeps policy unchanged or cuts rates
|
||||||
|
- Credit deterioration in the corporate loan book
|
||||||
|
- Adverse market conditions erode fee income
|
||||||
|
|
||||||
|
**Entry / Exit**
|
||||||
|
- **Target Entry**: $327.33 (current level)
|
||||||
|
- **Stop‑Loss**: $304.56 (7% below entry)
|
||||||
|
- **Take‑Profit**: $376.48 (15% above entry)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Goldman Sachs Group Inc. (GS) – **Long**
|
||||||
|
- **Conviction:** 0.72
|
||||||
|
- **Weight:** 0.08
|
||||||
|
- **Stop:** 0.07
|
||||||
|
- **Take:** 0.15
|
||||||
|
|
||||||
|
**Thesis**
|
||||||
|
GS will benefit from higher net interest income as rates rise and from heightened fee income driven by increased market volatility. Its trading and advisory businesses are well‑positioned to capture opportunities in a tightening monetary environment.
|
||||||
|
|
||||||
|
**Supporting Data**
|
||||||
|
- Price data not retrieved in this session; thesis relies on fundamentals and sector dynamics.
|
||||||
|
|
||||||
|
**Catalysts**
|
||||||
|
- July CPI and FOMC rate hike expectations
|
||||||
|
- Volatility spikes around the policy announcement
|
||||||
|
|
||||||
|
**Key Risks**
|
||||||
|
- Market calm reduces fee income
|
||||||
|
- Credit risk affecting loan book yields
|
||||||
|
- Regulatory changes impacting trading revenue
|
||||||
|
|
||||||
|
**Entry / Exit**
|
||||||
|
- **Target Entry**: Current market price (to be confirmed)
|
||||||
|
- **Stop‑Loss**: 7 % below entry
|
||||||
|
- **Take‑Profit**: 15 % above entry
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Bank of America Corp. (BAC) – **Long**
|
||||||
|
- **Conviction:** 0.72
|
||||||
|
- **Weight:** 0.08
|
||||||
|
- **Stop:** 0.07
|
||||||
|
- **Take:** 0.15
|
||||||
|
|
||||||
|
**Thesis**
|
||||||
|
BAC’s sizable retail loan base and strong capital profile position it to benefit from a higher yield environment. Net interest margins are expected to expand as the Fed raises rates in July 2026.
|
||||||
|
|
||||||
|
**Supporting Data**
|
||||||
|
- Price data not retrieved; thesis grounded in balance‑sheet metrics and macro drivers.
|
||||||
|
|
||||||
|
**Catalysts**
|
||||||
|
- July CPI release and subsequent FOMC decision
|
||||||
|
- Rising rates increasing interest income on retail and commercial loans
|
||||||
|
|
||||||
|
**Key Risks**
|
||||||
|
- Unfavorable credit developments in the retail segment
|
||||||
|
- Fed policy delay or dovish stance
|
||||||
|
- Regulatory constraints on lending rates
|
||||||
|
|
||||||
|
**Entry / Exit**
|
||||||
|
- **Target Entry**: Current market price (to be confirmed)
|
||||||
|
- **Stop‑Loss**: 7 % below entry
|
||||||
|
- **Take‑Profit**: 15 % above entry
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Portfolio Notes
|
||||||
|
This is a fresh account with no prior holdings. The three long positions in JPM, GS, and BAC collectively represent 24 % of the initial allocation (8 % each). The portfolio is well‑diversified across large‑cap U.S. banks, capturing both interest‑rate exposure and fee‑income upside while maintaining a moderate risk profile.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Summary Table
|
||||||
|
|
||||||
|
| Ticker | Dir | Conv | Weight | Stop | Take | One‑liner |
|
||||||
|
|--------|-----|------|--------|------|------|-----------|
|
||||||
|
| JPM | Long | 0.72 | 0.08 | 0.07 | 0.15 | JPM’s diversified loan mix and strong capital position will boost NII in a tightening cycle. |
|
||||||
|
| GS | Long | 0.72 | 0.08 | 0.07 | 0.15 | GS will capture higher NII and fee income from increased volatility under a higher‑rate regime. |
|
||||||
|
| BAC | Long | 0.72 | 0.08 | 0.07 | 0.15 | BAC’s large retail loan base and solid capital will benefit from a broader yield curve. |
|
||||||
|
|
||||||
|
---
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
# Daily Research — 2026-06-30
|
||||||
|
|
||||||
|
## Agents
|
||||||
|
|
||||||
|
### Atlas — Macro / Event-Driven Thematic — *complete*
|
||||||
|
- Picks: JPM(long,0.72), GS(long,0.72), BAC(long,0.72)
|
||||||
|
- Full note: [`atlas.md`](atlas.md)
|
||||||
|
|
||||||
|
### Nova — Momentum Growth Hunter — *complete*
|
||||||
|
- Picks: NVDA(long,0.72), AMD(long,0.72), MSFT(long,0.72)
|
||||||
|
- Full note: [`nova.md`](nova.md)
|
||||||
|
|
||||||
|
### Warren-lite — Patient Value / Contrarian — *complete*
|
||||||
|
- Picks: MO(long,0.72), KO(long,0.72)
|
||||||
|
- Full note: [`warren_lite.md`](warren_lite.md)
|
||||||
@@ -0,0 +1,122 @@
|
|||||||
|
%PDF-1.4
|
||||||
|
%“Œ‹ž ReportLab Generated PDF document (opensource)
|
||||||
|
1 0 obj
|
||||||
|
<<
|
||||||
|
/F1 2 0 R /F2 3 0 R /F3 4 0 R /F4 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
2 0 obj
|
||||||
|
<<
|
||||||
|
/BaseFont /Helvetica /Encoding /WinAnsiEncoding /Name /F1 /Subtype /Type1 /Type /Font
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
3 0 obj
|
||||||
|
<<
|
||||||
|
/BaseFont /Helvetica-Bold /Encoding /WinAnsiEncoding /Name /F2 /Subtype /Type1 /Type /Font
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
4 0 obj
|
||||||
|
<<
|
||||||
|
/BaseFont /Helvetica-BoldOblique /Encoding /WinAnsiEncoding /Name /F3 /Subtype /Type1 /Type /Font
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
5 0 obj
|
||||||
|
<<
|
||||||
|
/BaseFont /Courier /Encoding /WinAnsiEncoding /Name /F4 /Subtype /Type1 /Type /Font
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
6 0 obj
|
||||||
|
<<
|
||||||
|
/Contents 16 0 R /MediaBox [ 0 0 595.2756 841.8898 ] /Parent 15 0 R /Resources <<
|
||||||
|
/Font 1 0 R /ProcSet [ /PDF /Text /ImageB /ImageC /ImageI ]
|
||||||
|
>> /Rotate 0 /Trans <<
|
||||||
|
|
||||||
|
>>
|
||||||
|
/Type /Page
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
7 0 obj
|
||||||
|
<<
|
||||||
|
/Outlines 9 0 R /PageMode /UseNone /Pages 15 0 R /Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
8 0 obj
|
||||||
|
<<
|
||||||
|
/Author () /CreationDate (D:20260701220446-05'00') /Creator (\(unspecified\)) /Keywords () /ModDate (D:20260701220446-05'00') /Producer (xhtml2pdf <https://github.com/xhtml2pdf/xhtml2pdf/>)
|
||||||
|
/Subject () /Title () /Trapped /False
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
9 0 obj
|
||||||
|
<<
|
||||||
|
/Count 3 /First 10 0 R /Last 10 0 R /Type /Outlines
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
10 0 obj
|
||||||
|
<<
|
||||||
|
/Count -1 /Dest [ 6 0 R /Fit ] /First 11 0 R /Last 11 0 R /Parent 9 0 R /Title (Daily Research \204 2026-06-30)
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
11 0 obj
|
||||||
|
<<
|
||||||
|
/Count -3 /Dest [ 6 0 R /Fit ] /First 12 0 R /Last 14 0 R /Parent 10 0 R /Title (Agents)
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
12 0 obj
|
||||||
|
<<
|
||||||
|
/Dest [ 6 0 R /Fit ] /Next 13 0 R /Parent 11 0 R /Title (Atlas \204 Macro / Event-Driven Thematic \204 complete)
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
13 0 obj
|
||||||
|
<<
|
||||||
|
/Dest [ 6 0 R /Fit ] /Next 14 0 R /Parent 11 0 R /Prev 12 0 R /Title (Nova \204 Momentum Growth Hunter \204 complete)
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
14 0 obj
|
||||||
|
<<
|
||||||
|
/Dest [ 6 0 R /Fit ] /Parent 11 0 R /Prev 13 0 R /Title (Warren-lite \204 Patient Value / Contrarian \204 complete)
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
15 0 obj
|
||||||
|
<<
|
||||||
|
/Count 1 /Kids [ 6 0 R ] /Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
16 0 obj
|
||||||
|
<<
|
||||||
|
/Filter [ /ASCII85Decode /FlateDecode ] /Length 779
|
||||||
|
>>
|
||||||
|
stream
|
||||||
|
Gb!;agMY_1&:N^lk)iVOf__iC7REb_9*2q%?]Zaq\j5RSCJ7S(^:T,?E<8j*R\V\snp!9Dk0,hl$H+!m\.3#!;]k9V$C$,A%B)Mmr[0]"/&*PsHdZI`1WY7_$FK>b#gVhL)ilGtkV@aA-miA/Z%_FW/]pq3n7,i7V-BYM@R-boP8$MFJnd*/7(7+M:;\1nNa]_j#"Fu[&QRlP#lO/L^N8%R"h*7tKMnP;Ae.3PKehd#7"S<Mks/.#7^$5uA0n%2;4-Fn4,D8!iciqUTQVS+`@EZ6DGV8+%tP#_4/"HI1$g5'4mLZO,PP"hYlHE'OhV%e3=OZPo-ftYM!Y>_/;6-Thq1tEc"pkh_&nGPOACUhPbPp=Mm!IfLM?ZB$M3c'ZQOYa/@O7d-mUCuE.To>dQR5-5H>k!4f]'kecdr>%(55LQc;]:Enec`WF_`1CgD9kYr`j&R%D*KDJ)e*dU[Gn:p\!c0sphS^hMhF/+QMse6a3g1)qk]Ye!Oo(fZ/j;A=FF3'G5gCq8r#POVS-D9\d\Ou*\Frod$lA^M\PE.Z((lQKfqT:d#N[9Jbh[b+#p'.b/i`n">"(tr+Rcc>O(QK2KJ>h2g@Mt6/=Gm8%i>W3TJ.lBB4;P_c[q1lM=$)Dq1rANHMD7N_e=(8T6(isP5H#J<,1huJ$mU)T=/.7YF1s*XhO1KMDZ6k4i+I\"-2`jZr4:AGdO?AlUUum"g)#oTA3gV-gICq\`;,V_]p'gak3/UUNDhrjV8'MEmIW5F;KX!Eam8]hM)jOQb_>~>endstream
|
||||||
|
endobj
|
||||||
|
xref
|
||||||
|
0 17
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000061 00000 n
|
||||||
|
0000000122 00000 n
|
||||||
|
0000000229 00000 n
|
||||||
|
0000000341 00000 n
|
||||||
|
0000000460 00000 n
|
||||||
|
0000000565 00000 n
|
||||||
|
0000000770 00000 n
|
||||||
|
0000000855 00000 n
|
||||||
|
0000001107 00000 n
|
||||||
|
0000001180 00000 n
|
||||||
|
0000001314 00000 n
|
||||||
|
0000001425 00000 n
|
||||||
|
0000001560 00000 n
|
||||||
|
0000001700 00000 n
|
||||||
|
0000001838 00000 n
|
||||||
|
0000001898 00000 n
|
||||||
|
trailer
|
||||||
|
<<
|
||||||
|
/ID
|
||||||
|
[<f68362ebe35110a7ab4d57218c7b3c72><f68362ebe35110a7ab4d57218c7b3c72>]
|
||||||
|
% ReportLab generated PDF document -- digest (opensource)
|
||||||
|
|
||||||
|
/Info 8 0 R
|
||||||
|
/Root 7 0 R
|
||||||
|
/Size 17
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
2768
|
||||||
|
%%EOF
|
||||||
File diff suppressed because it is too large.
Load diff
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 15 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 63 KiB |
Binary file not shown.
|
After Width: | Height: | Size: 36 KiB |
@@ -0,0 +1,56 @@
|
|||||||
|
import yfinance as yf
|
||||||
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
# Tickers
|
||||||
|
main = 'MRVL'
|
||||||
|
peers = ['AVGO','AMD','NVDA']
|
||||||
|
lookback = '180d'
|
||||||
|
|
||||||
|
# Download data
|
||||||
|
df_main = yf.download(main, period=lookback, progress=False)
|
||||||
|
df_peers = {t: yf.download(t, period=lookback, progress=False) for t in peers}
|
||||||
|
|
||||||
|
# Technical indicators for main
|
||||||
|
for ma in [20,50,200]:
|
||||||
|
df_main[f'SMA{ma}'] = df_main['Close'].rolling(ma).mean()
|
||||||
|
|
||||||
|
# Return and volatility
|
||||||
|
df_main['Ret'] = df_main['Close'].pct_change()
|
||||||
|
df_main['Vol20'] = df_main['Ret'].rolling(20).std() * np.sqrt(252)
|
||||||
|
|
||||||
|
# Correlation with peers
|
||||||
|
corr_df = pd.DataFrame(index=df_main.index)
|
||||||
|
for t in peers:
|
||||||
|
corr_df[f'{t}_corr'] = df_main['Ret'].rolling(60).corr(df_peers[t]['Close'].pct_change())
|
||||||
|
|
||||||
|
# Plot price + MA
|
||||||
|
plt.figure(figsize=(12,6))
|
||||||
|
plt.plot(df_main['Close'], label='Close')
|
||||||
|
plt.plot(df_main['SMA20'], label='SMA20')
|
||||||
|
plt.plot(df_main['SMA50'], label='SMA50')
|
||||||
|
plt.plot(df_main['SMA200'], label='SMA200')
|
||||||
|
plt.title(f'{main} price with moving averages')
|
||||||
|
plt.legend()
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig('mrvl_ma.png')
|
||||||
|
|
||||||
|
# Plot volatility
|
||||||
|
plt.figure(figsize=(12,4))
|
||||||
|
plt.plot(df_main['Vol20'], label='20‑Day Vol (annualized)')
|
||||||
|
plt.title(f'{main} 20‑Day volatility')
|
||||||
|
plt.legend()
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig('mrvl_vol.png')
|
||||||
|
|
||||||
|
# Plot correlation heatmap
|
||||||
|
plt.figure(figsize=(8,6))
|
||||||
|
corr_vals = corr_df.mean().values
|
||||||
|
plt.bar(peers, corr_vals)
|
||||||
|
plt.title('Average 60‑day correlation of MRVL with peers')
|
||||||
|
plt.ylabel('Correlation')
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig('mrvl_corr.png')
|
||||||
|
|
||||||
|
print('Charts generated: mrvl_ma.png, mrvl_vol.png, mrvl_corr.png')
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,48 @@
|
|||||||
|
import yfinance as yf
|
||||||
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
stock='NVDA'
|
||||||
|
sector='SMH'
|
||||||
|
lookback='365d'
|
||||||
|
|
||||||
|
# Download
|
||||||
|
s= yf.download(stock, period=lookback, progress=False)
|
||||||
|
se= yf.download(sector, period=lookback, progress=False)
|
||||||
|
|
||||||
|
s['SMA20']=s['Close'].rolling(20).mean()
|
||||||
|
s['SMA50']=s['Close'].rolling(50).mean()
|
||||||
|
s['SMA200']=s['Close'].rolling(200).mean()
|
||||||
|
s['Ret']=s['Close'].pct_change()
|
||||||
|
s['Vol20']=s['Ret'].rolling(20).std()*np.sqrt(252)
|
||||||
|
|
||||||
|
# correlation
|
||||||
|
merged=pd.merge(s[['Close']], se[['Close']], left_index=True, right_index=True, suffixes=('','_etf'))
|
||||||
|
merged['Ret']=merged['Close'].pct_change()
|
||||||
|
merged['Ret_etf']=merged['Close_etf'].pct_change()
|
||||||
|
merged['Corr']=merged['Ret'].rolling(60).corr(merged['Ret_etf'])
|
||||||
|
|
||||||
|
# plots
|
||||||
|
plt.figure(figsize=(12,6))
|
||||||
|
plt.plot(s['Close'],label='Close')
|
||||||
|
plt.plot(s['SMA20'],label='SMA20')
|
||||||
|
plt.plot(s['SMA50'],label='SMA50')
|
||||||
|
plt.plot(s['SMA200'],label='SMA200')
|
||||||
|
plt.legend()
|
||||||
|
plt.title('NVDA price & moving averages')
|
||||||
|
plt.savefig('nvda_ma.png')
|
||||||
|
|
||||||
|
plt.figure(figsize=(12,4))
|
||||||
|
plt.plot(s['Vol20'],label='20‑Day Vol (annualized)')
|
||||||
|
plt.legend()
|
||||||
|
plt.title('NVDA volatility')
|
||||||
|
plt.savefig('nvda_vol.png')
|
||||||
|
|
||||||
|
plt.figure(figsize=(12,4))
|
||||||
|
plt.plot(merged['Corr'],label='60‑Day Correlation with SMH')
|
||||||
|
plt.legend()
|
||||||
|
plt.title('NVDA vs SMH correlation')
|
||||||
|
plt.savefig('nvda_corr.png')
|
||||||
|
|
||||||
|
print('Done')
|
||||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 32 KiB |
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,7 @@
|
|||||||
|
|
||||||
|
import numpy as np, pandas as pd, matplotlib.pyplot as plt
|
||||||
|
import yfinance as yf
|
||||||
|
df = yf.download("AAPL", period="3mo", progress=False)
|
||||||
|
close = df["Close"].squeeze() # handle yfinance multiindex
|
||||||
|
print("AAPL last:", round(float(close.iloc[-1]),2), "| 20d SMA:", round(float(close.rolling(20).mean().iloc[-1]),2))
|
||||||
|
close.plot(title="AAPL 3mo"); plt.savefig("aapl.png")
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,45 @@
|
|||||||
|
import yfinance as yf
|
||||||
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
stock='NVDA'
|
||||||
|
sector='SMH'
|
||||||
|
lookback='365d'
|
||||||
|
|
||||||
|
s= yf.download(stock, period=lookback, progress=False)
|
||||||
|
se= yf.download(sector, period=lookback, progress=False)
|
||||||
|
|
||||||
|
s['SMA20']=s['Close'].rolling(20).mean()
|
||||||
|
s['SMA50']=s['Close'].rolling(50).mean()
|
||||||
|
s['SMA200']=s['Close'].rolling(200).mean()
|
||||||
|
s['Ret']=s['Close'].pct_change()
|
||||||
|
s['Vol20']=s['Ret'].rolling(20).std()*np.sqrt(252)
|
||||||
|
|
||||||
|
merged=pd.merge(s[['Close','Ret']], se[['Close']], left_index=True, right_index=True, suffixes=('','_etf'))
|
||||||
|
merged['Ret_etf']=merged['Close_etf'].pct_change()
|
||||||
|
merged['Corr']=merged['Ret'].rolling(60).corr(merged['Ret_etf'])
|
||||||
|
|
||||||
|
# plots
|
||||||
|
plt.figure(figsize=(12,6))
|
||||||
|
plt.plot(s['Close'],label='Close')
|
||||||
|
plt.plot(s['SMA20'],label='SMA20')
|
||||||
|
plt.plot(s['SMA50'],label='SMA50')
|
||||||
|
plt.plot(s['SMA200'],label='SMA200')
|
||||||
|
plt.legend()
|
||||||
|
plt.title('NVDA price & moving averages')
|
||||||
|
plt.savefig('nvda_ma.png')
|
||||||
|
|
||||||
|
plt.figure(figsize=(12,4))
|
||||||
|
plt.plot(s['Vol20'],label='20‑Day Vol (annualized)')
|
||||||
|
plt.legend()
|
||||||
|
plt.title('NVDA volatility')
|
||||||
|
plt.savefig('nvda_vol.png')
|
||||||
|
|
||||||
|
plt.figure(figsize=(12,4))
|
||||||
|
plt.plot(merged['Corr'],label='60‑Day Correlation with SMH')
|
||||||
|
plt.legend()
|
||||||
|
plt.title('NVDA vs SMH correlation')
|
||||||
|
plt.savefig('nvda_corr.png')
|
||||||
|
|
||||||
|
print('Done')
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
import pandas, numpy; print('libs ok')
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
import os; print("SECRET=", os.environ.get("ALPACA_NOVA_SECRET"))
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
import os; print(os.environ.get('ALPACA_NOVA_SECRET'))
|
||||||
Binary file not shown.
Binary file not shown.
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,6 @@
|
|||||||
|
|
||||||
|
import yfinance as yf, matplotlib.pyplot as plt
|
||||||
|
df = yf.download("AAPL", period="3mo", progress=False)
|
||||||
|
df["SMA20"] = df["Close"].rolling(20).mean()
|
||||||
|
print("rows", len(df), "last close", round(float(df["Close"].iloc[-1]),2))
|
||||||
|
df[["Close","SMA20"]].plot(title="AAPL"); plt.savefig("aapl.png")
|
||||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,55 @@
|
|||||||
|
import yfinance as yf
|
||||||
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
|
||||||
|
# Define tickers
|
||||||
|
stock = 'NVDA'
|
||||||
|
sector_etf = 'SMH'
|
||||||
|
lookback = 365 # days
|
||||||
|
|
||||||
|
# Download daily data
|
||||||
|
stock_df = yf.download(stock, period=f'{lookback}d', progress=False)
|
||||||
|
sector_df = yf.download(sector_etf, period=f'{lookback}d', progress=False)
|
||||||
|
|
||||||
|
# Calculate technical indicators
|
||||||
|
stock_df['SMA20'] = stock_df['Close'].rolling(window=20).mean()
|
||||||
|
stock_df['SMA50'] = stock_df['Close'].rolling(window=50).mean()
|
||||||
|
stock_df['SMA200'] = stock_df['Close'].rolling(window=200).mean()
|
||||||
|
stock_df['DailyRet'] = stock_df['Close'].pct_change()
|
||||||
|
stock_df['Vol20'] = stock_df['DailyRet'].rolling(window=20).std() * np.sqrt(252) # annualized
|
||||||
|
|
||||||
|
# Correlation with sector ETF
|
||||||
|
merged = pd.merge(stock_df[['Close']], sector_df[['Close']], left_index=True, right_index=True, suffixes=('_stock', '_etf'))
|
||||||
|
merged['DailyRet_stock'] = merged['Close_stock'].pct_change()
|
||||||
|
merged['DailyRet_etf'] = merged['Close_etf'].pct_change()
|
||||||
|
corr = merged['DailyRet_stock'].rolling(window=60).corr(merged['DailyRet_etf'])
|
||||||
|
|
||||||
|
# Plot price with SMAs
|
||||||
|
plt.figure(figsize=(12,6))
|
||||||
|
plt.plot(stock_df['Close'], label=f'{stock} Close')
|
||||||
|
plt.plot(stock_df['SMA20'], label='SMA20')
|
||||||
|
plt.plot(stock_df['SMA50'], label='SMA50')
|
||||||
|
plt.plot(stock_df['SMA200'], label='SMA200')
|
||||||
|
plt.title(f'{stock} Price with Moving Averages')
|
||||||
|
plt.legend()
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig('nvda_ma_chart.png')
|
||||||
|
|
||||||
|
# Plot volatility
|
||||||
|
plt.figure(figsize=(12,4))
|
||||||
|
plt.plot(stock_df['Vol20'], label='20‑Day Vol (annualized)')
|
||||||
|
plt.title(f'{stock} 20‑Day Volatility')
|
||||||
|
plt.legend()
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig('nvda_vol_chart.png')
|
||||||
|
|
||||||
|
# Plot correlation
|
||||||
|
plt.figure(figsize=(12,4))
|
||||||
|
plt.plot(corr, label='60‑Day Correlation with SMH')
|
||||||
|
plt.title(f'{stock} vs SMH Correlation')
|
||||||
|
plt.legend()
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.savefig('nvda_corr_chart.png')
|
||||||
|
|
||||||
|
print('Charts generated: nvda_ma_chart.png, nvda_vol_chart.png, nvda_corr_chart.png')
|
||||||
@@ -0,0 +1,28 @@
|
|||||||
|
"""Sandbox security tests — the critical property is that model-generated code cannot read
|
||||||
|
account secrets. Pure (no network)."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
|
||||||
|
from tools.sandbox import scrubbed_env
|
||||||
|
|
||||||
|
|
||||||
|
def test_scrubbed_env_excludes_secrets(monkeypatch):
|
||||||
|
monkeypatch.setenv("ALPACA_NOVA_SECRET", "topsecret")
|
||||||
|
monkeypatch.setenv("FINNHUB_API_KEY", "fhkey")
|
||||||
|
monkeypatch.setenv("OMLX_API_KEY", "omlxkey")
|
||||||
|
env = scrubbed_env("/tmp/work")
|
||||||
|
# no secret-prefixed vars leak through
|
||||||
|
assert all(
|
||||||
|
not k.upper().startswith(("ALPACA", "FINNHUB", "OMLX", "EMAIL", "SMTP"))
|
||||||
|
for k in env
|
||||||
|
)
|
||||||
|
assert "ALPACA_NOVA_SECRET" not in env
|
||||||
|
|
||||||
|
|
||||||
|
def test_scrubbed_env_minimal_and_headless():
|
||||||
|
env = scrubbed_env("/home/x")
|
||||||
|
assert env["HOME"] == "/home/x"
|
||||||
|
assert env["MPLBACKEND"] == "Agg" # headless matplotlib
|
||||||
|
assert "PATH" in env
|
||||||
+3
-2
@@ -10,12 +10,13 @@ from tools.market_data import get_bars, get_fundamentals, get_news, get_quote
|
|||||||
from tools.options import get_option_chain, get_option_quote
|
from tools.options import get_option_chain, get_option_quote
|
||||||
from tools.portfolio import make_get_portfolio
|
from tools.portfolio import make_get_portfolio
|
||||||
from tools.python_exec import run_python
|
from tools.python_exec import run_python
|
||||||
|
from tools.sandbox import run_analysis
|
||||||
from tools.web import web_fetch, web_search
|
from tools.web import web_fetch, web_search
|
||||||
|
|
||||||
# Tools that are the same for every persona (portfolio is added per-persona below).
|
# Tools that are the same for every persona (portfolio is added per-persona below).
|
||||||
BASE_TOOLS = [
|
BASE_TOOLS = [
|
||||||
run_python, web_search, web_fetch, get_quote, get_bars, get_news, get_fundamentals,
|
run_python, run_analysis, web_search, web_fetch, get_quote, get_bars, get_news,
|
||||||
get_option_chain, get_option_quote,
|
get_fundamentals, get_option_chain, get_option_quote,
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,13 +1,14 @@
|
|||||||
"""run_python — the calculator. Agents call this for ALL arithmetic instead of doing
|
"""run_python — the calculator. Agents call this for ALL arithmetic instead of doing
|
||||||
mental math (LLMs are unreliable at it). Executes in an isolated subprocess with a hard
|
mental math (LLMs are unreliable at it). Executes in an isolated subprocess with a hard
|
||||||
timeout. Kept math-only by convention; runs model-generated code on this machine, so
|
timeout and a **scrubbed environment** (no account secrets), so model-generated code can't
|
||||||
scope is deliberately narrow.
|
read API keys. For heavier data/analysis work, agents use run_analysis (tools/sandbox.py).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from __future__ import annotations
|
from __future__ import annotations
|
||||||
|
|
||||||
import subprocess
|
import subprocess
|
||||||
import sys
|
import sys
|
||||||
|
import tempfile
|
||||||
|
|
||||||
from langchain_core.tools import tool
|
from langchain_core.tools import tool
|
||||||
|
|
||||||
@@ -20,14 +21,17 @@ def run_python(code: str) -> str:
|
|||||||
|
|
||||||
Use for ANY arithmetic: position sizing, percentages, P/E, returns, averages, ratios.
|
Use for ANY arithmetic: position sizing, percentages, P/E, returns, averages, ratios.
|
||||||
You MUST print() the result. `math` and `statistics` are importable.
|
You MUST print() the result. `math` and `statistics` are importable.
|
||||||
Example: `print(round(1000 / 23.55, 4))`. No network or file access; 5s limit.
|
Example: `print(round(1000 / 23.55, 4))`. No access to secrets; 5s limit.
|
||||||
"""
|
"""
|
||||||
|
from tools.sandbox import scrubbed_env
|
||||||
|
|
||||||
try:
|
try:
|
||||||
proc = subprocess.run(
|
proc = subprocess.run(
|
||||||
[sys.executable, "-I", "-c", code],
|
[sys.executable, "-I", "-c", code],
|
||||||
capture_output=True,
|
capture_output=True,
|
||||||
text=True,
|
text=True,
|
||||||
timeout=_TIMEOUT_S,
|
timeout=_TIMEOUT_S,
|
||||||
|
env=scrubbed_env(tempfile.gettempdir()),
|
||||||
)
|
)
|
||||||
except subprocess.TimeoutExpired:
|
except subprocess.TimeoutExpired:
|
||||||
return f"ERROR: execution timed out ({_TIMEOUT_S}s limit)."
|
return f"ERROR: execution timed out ({_TIMEOUT_S}s limit)."
|
||||||
|
|||||||
@@ -0,0 +1,100 @@
|
|||||||
|
"""Analyst script sandbox — run_analysis.
|
||||||
|
|
||||||
|
Lets an agent write and run a real Python analysis script (pandas/numpy/yfinance/matplotlib)
|
||||||
|
like a research analyst: fetch data, compute indicators/stats/backtests, save charts. Runs in
|
||||||
|
a throwaway temp workdir with a **scrubbed environment** (no ALPACA_/FINNHUB_/OMLX_ secrets —
|
||||||
|
model-generated code must never see keys), a wall-clock timeout, and network access (for data).
|
||||||
|
|
||||||
|
Security posture (honest): running model-written networked code is a real surface. Mitigations:
|
||||||
|
scrubbed env (no secrets), isolated temp workdir (no repo writes), wall-clock timeout, truncated
|
||||||
|
output. Acceptable on a personal machine + local model; not a hardened multi-tenant sandbox.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
import tempfile
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from langchain_core.tools import tool
|
||||||
|
|
||||||
|
from config.settings import get_settings
|
||||||
|
|
||||||
|
_STDOUT_LIMIT = 4000
|
||||||
|
_SECRET_PREFIXES = ("ALPACA", "FINNHUB", "OMLX", "EMAIL", "SMTP", "OPENAI", "ANTHROPIC")
|
||||||
|
_ARTIFACT_EXTS = {".png", ".jpg", ".jpeg", ".svg", ".csv", ".txt", ".html", ".md", ".json"}
|
||||||
|
|
||||||
|
|
||||||
|
def scrubbed_env(home: str) -> dict[str, str]:
|
||||||
|
"""A minimal environment with NO secrets — built from scratch, not inherited."""
|
||||||
|
env = {
|
||||||
|
"PATH": os.environ.get("PATH", "/usr/bin:/bin"),
|
||||||
|
"HOME": home,
|
||||||
|
"TMPDIR": home,
|
||||||
|
"LANG": os.environ.get("LANG", "en_US.UTF-8"),
|
||||||
|
"MPLBACKEND": "Agg", # headless matplotlib
|
||||||
|
"MPLCONFIGDIR": home,
|
||||||
|
}
|
||||||
|
# defensive: never leak anything secret-looking even if the whitelist grows
|
||||||
|
return {k: v for k, v in env.items() if not k.upper().startswith(_SECRET_PREFIXES)}
|
||||||
|
|
||||||
|
|
||||||
|
@tool
|
||||||
|
def run_analysis(code: str) -> str:
|
||||||
|
"""Run a Python analysis script and return its stdout plus any files it wrote.
|
||||||
|
|
||||||
|
Available: pandas, numpy, yfinance (data), matplotlib (charts, Agg), scipy. Use this like a
|
||||||
|
real analyst: fetch data with yfinance, compute indicators/stats/backtests, and save charts
|
||||||
|
with `plt.savefig("chart.png")`. You MUST print() anything you want to read back. Runs in an
|
||||||
|
isolated temp dir with a ~90s limit and NO access to account secrets.
|
||||||
|
|
||||||
|
Example:
|
||||||
|
import yfinance as yf, matplotlib.pyplot as plt
|
||||||
|
df = yf.download("AAPL", period="6mo")
|
||||||
|
df["SMA50"] = df["Close"].rolling(50).mean()
|
||||||
|
print(df[["Close","SMA50"]].tail())
|
||||||
|
df[["Close","SMA50"]].plot(); plt.savefig("aapl.png")
|
||||||
|
"""
|
||||||
|
s = get_settings()
|
||||||
|
s.ensure_dirs()
|
||||||
|
workdir = Path(tempfile.mkdtemp(dir=s.sandbox_dir))
|
||||||
|
(workdir / "script.py").write_text(code)
|
||||||
|
# shared, persistent matplotlib cache so the font cache is built once (not per run) and
|
||||||
|
# doesn't land in each run's artifact list
|
||||||
|
mpl_cache = s.sandbox_dir / ".mplconfig"
|
||||||
|
mpl_cache.mkdir(parents=True, exist_ok=True)
|
||||||
|
env = scrubbed_env(str(workdir))
|
||||||
|
env["MPLCONFIGDIR"] = str(mpl_cache)
|
||||||
|
try:
|
||||||
|
proc = subprocess.run(
|
||||||
|
[sys.executable, "script.py"],
|
||||||
|
cwd=workdir,
|
||||||
|
env=env,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=s.sandbox_timeout_s,
|
||||||
|
)
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
return f"ERROR: analysis timed out ({s.sandbox_timeout_s}s limit)."
|
||||||
|
except Exception as e: # pragma: no cover - defensive
|
||||||
|
return f"ERROR: {type(e).__name__}: {e}"
|
||||||
|
|
||||||
|
out = (proc.stdout or "").strip()
|
||||||
|
err = (proc.stderr or "").strip()
|
||||||
|
artifacts = [
|
||||||
|
str(p) for p in sorted(workdir.iterdir())
|
||||||
|
if p.is_file() and p.name != "script.py" and p.suffix.lower() in _ARTIFACT_EXTS
|
||||||
|
]
|
||||||
|
|
||||||
|
if proc.returncode != 0:
|
||||||
|
return f"ERROR (exit {proc.returncode}): {err[-1500:] or 'no stderr'}"
|
||||||
|
result = out[:_STDOUT_LIMIT] or "(no stdout — did you print()?)"
|
||||||
|
if len(out) > _STDOUT_LIMIT:
|
||||||
|
result += "\n...[truncated]"
|
||||||
|
if err:
|
||||||
|
result += f"\n[stderr] {err[-400:]}"
|
||||||
|
if artifacts:
|
||||||
|
result += "\nArtifacts written: " + ", ".join(artifacts)
|
||||||
|
return result
|
||||||
@@ -75,6 +75,24 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/b0/7b/90df4a0a816d98d6ea26f559d87836d494a2cf1fcf063be67df50a7bcc30/anyio-4.14.1-py3-none-any.whl", hash = "sha256:4e5533c5b8ff0a24f5d7a176cbe6877129cd183893f66b537f8f227d10527d72", size = 124875, upload-time = "2026-06-24T20:56:04.413Z" },
|
{ url = "https://files.pythonhosted.org/packages/b0/7b/90df4a0a816d98d6ea26f559d87836d494a2cf1fcf063be67df50a7bcc30/anyio-4.14.1-py3-none-any.whl", hash = "sha256:4e5533c5b8ff0a24f5d7a176cbe6877129cd183893f66b537f8f227d10527d72", size = 124875, upload-time = "2026-06-24T20:56:04.413Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "arabic-reshaper"
|
||||||
|
version = "3.0.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/f2/6c/6be41c689bd2f6e9274c77bf45be820e41ff1ecb9ab0a1d23964b6d04606/arabic_reshaper-3.0.1.tar.gz", hash = "sha256:a0d9b2a9fa29b5f2c1d705f407adf6ca4242405b9cac0e5cc09e6c4f3f8fb68c", size = 42829, upload-time = "2026-04-28T13:53:17.447Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6e/7d/77752d819ede528c664ecf4de974b17b049e054a14537b74b53c8568014f/arabic_reshaper-3.0.1-py3-none-any.whl", hash = "sha256:41c5adc2420f85758eada7e880251c4b6a2adbd83377bd27e5d4eba71f648bc7", size = 20699, upload-time = "2026-04-28T13:53:15.894Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "asn1crypto"
|
||||||
|
version = "1.5.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/de/cf/d547feed25b5244fcb9392e288ff9fdc3280b10260362fc45d37a798a6ee/asn1crypto-1.5.1.tar.gz", hash = "sha256:13ae38502be632115abf8a24cbe5f4da52e3b5231990aff31123c805306ccb9c", size = 121080, upload-time = "2022-03-15T14:46:52.889Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c9/7f/09065fd9e27da0eda08b4d6897f1c13535066174cc023af248fc2a8d5e5a/asn1crypto-1.5.1-py2.py3-none-any.whl", hash = "sha256:db4e40728b728508912cbb3d44f19ce188f218e9eba635821bb4b68564f8fd67", size = 105045, upload-time = "2022-03-15T14:46:51.055Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "attrs"
|
name = "attrs"
|
||||||
version = "26.1.0"
|
version = "26.1.0"
|
||||||
@@ -279,6 +297,45 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
{ url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "contourpy"
|
||||||
|
version = "1.3.3"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
|
||||||
|
{ name = "numpy", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/58/01/1253e6698a07380cd31a736d248a3f2a50a7c88779a1813da27503cadc2a/contourpy-1.3.3.tar.gz", hash = "sha256:083e12155b210502d0bca491432bb04d56dc3432f95a979b429f2848c3dbe880", size = 13466174, upload-time = "2025-07-26T12:03:12.549Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/91/2e/c4390a31919d8a78b90e8ecf87cd4b4c4f05a5b48d05ec17db8e5404c6f4/contourpy-1.3.3-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:709a48ef9a690e1343202916450bc48b9e51c049b089c7f79a267b46cffcdaa1", size = 288773, upload-time = "2025-07-26T12:01:02.277Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0d/44/c4b0b6095fef4dc9c420e041799591e3b63e9619e3044f7f4f6c21c0ab24/contourpy-1.3.3-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:23416f38bfd74d5d28ab8429cc4d63fa67d5068bd711a85edb1c3fb0c3e2f381", size = 270149, upload-time = "2025-07-26T12:01:04.072Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/30/2e/dd4ced42fefac8470661d7cb7e264808425e6c5d56d175291e93890cce09/contourpy-1.3.3-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:929ddf8c4c7f348e4c0a5a3a714b5c8542ffaa8c22954862a46ca1813b667ee7", size = 329222, upload-time = "2025-07-26T12:01:05.688Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f2/74/cc6ec2548e3d276c71389ea4802a774b7aa3558223b7bade3f25787fafc2/contourpy-1.3.3-cp311-cp311-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:9e999574eddae35f1312c2b4b717b7885d4edd6cb46700e04f7f02db454e67c1", size = 377234, upload-time = "2025-07-26T12:01:07.054Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/03/b3/64ef723029f917410f75c09da54254c5f9ea90ef89b143ccadb09df14c15/contourpy-1.3.3-cp311-cp311-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:0bf67e0e3f482cb69779dd3061b534eb35ac9b17f163d851e2a547d56dba0a3a", size = 380555, upload-time = "2025-07-26T12:01:08.801Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5f/4b/6157f24ca425b89fe2eb7e7be642375711ab671135be21e6faa100f7448c/contourpy-1.3.3-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:51e79c1f7470158e838808d4a996fa9bac72c498e93d8ebe5119bc1e6becb0db", size = 355238, upload-time = "2025-07-26T12:01:10.319Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/98/56/f914f0dd678480708a04cfd2206e7c382533249bc5001eb9f58aa693e200/contourpy-1.3.3-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:598c3aaece21c503615fd59c92a3598b428b2f01bfb4b8ca9c4edeecc2438620", size = 1326218, upload-time = "2025-07-26T12:01:12.659Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fb/d7/4a972334a0c971acd5172389671113ae82aa7527073980c38d5868ff1161/contourpy-1.3.3-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:322ab1c99b008dad206d406bb61d014cf0174df491ae9d9d0fac6a6fda4f977f", size = 1392867, upload-time = "2025-07-26T12:01:15.533Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/75/3e/f2cc6cd56dc8cff46b1a56232eabc6feea52720083ea71ab15523daab796/contourpy-1.3.3-cp311-cp311-win32.whl", hash = "sha256:fd907ae12cd483cd83e414b12941c632a969171bf90fc937d0c9f268a31cafff", size = 183677, upload-time = "2025-07-26T12:01:17.088Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/98/4b/9bd370b004b5c9d8045c6c33cf65bae018b27aca550a3f657cdc99acdbd8/contourpy-1.3.3-cp311-cp311-win_amd64.whl", hash = "sha256:3519428f6be58431c56581f1694ba8e50626f2dd550af225f82fb5f5814d2a42", size = 225234, upload-time = "2025-07-26T12:01:18.256Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d9/b6/71771e02c2e004450c12b1120a5f488cad2e4d5b590b1af8bad060360fe4/contourpy-1.3.3-cp311-cp311-win_arm64.whl", hash = "sha256:15ff10bfada4bf92ec8b31c62bf7c1834c244019b4a33095a68000d7075df470", size = 193123, upload-time = "2025-07-26T12:01:19.848Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/be/45/adfee365d9ea3d853550b2e735f9d66366701c65db7855cd07621732ccfc/contourpy-1.3.3-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:b08a32ea2f8e42cf1d4be3169a98dd4be32bafe4f22b6c4cb4ba810fa9e5d2cb", size = 293419, upload-time = "2025-07-26T12:01:21.16Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/53/3e/405b59cfa13021a56bba395a6b3aca8cec012b45bf177b0eaf7a202cde2c/contourpy-1.3.3-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:556dba8fb6f5d8742f2923fe9457dbdd51e1049c4a43fd3986a0b14a1d815fc6", size = 273979, upload-time = "2025-07-26T12:01:22.448Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d4/1c/a12359b9b2ca3a845e8f7f9ac08bdf776114eb931392fcad91743e2ea17b/contourpy-1.3.3-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:92d9abc807cf7d0e047b95ca5d957cf4792fcd04e920ca70d48add15c1a90ea7", size = 332653, upload-time = "2025-07-26T12:01:24.155Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/63/12/897aeebfb475b7748ea67b61e045accdfcf0d971f8a588b67108ed7f5512/contourpy-1.3.3-cp312-cp312-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b2e8faa0ed68cb29af51edd8e24798bb661eac3bd9f65420c1887b6ca89987c8", size = 379536, upload-time = "2025-07-26T12:01:25.91Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/43/8a/a8c584b82deb248930ce069e71576fc09bd7174bbd35183b7943fb1064fd/contourpy-1.3.3-cp312-cp312-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:626d60935cf668e70a5ce6ff184fd713e9683fb458898e4249b63be9e28286ea", size = 384397, upload-time = "2025-07-26T12:01:27.152Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cc/8f/ec6289987824b29529d0dfda0d74a07cec60e54b9c92f3c9da4c0ac732de/contourpy-1.3.3-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:4d00e655fcef08aba35ec9610536bfe90267d7ab5ba944f7032549c55a146da1", size = 362601, upload-time = "2025-07-26T12:01:28.808Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/05/0a/a3fe3be3ee2dceb3e615ebb4df97ae6f3828aa915d3e10549ce016302bd1/contourpy-1.3.3-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:451e71b5a7d597379ef572de31eeb909a87246974d960049a9848c3bc6c41bf7", size = 1331288, upload-time = "2025-07-26T12:01:31.198Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/33/1d/acad9bd4e97f13f3e2b18a3977fe1b4a37ecf3d38d815333980c6c72e963/contourpy-1.3.3-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:459c1f020cd59fcfe6650180678a9993932d80d44ccde1fa1868977438f0b411", size = 1403386, upload-time = "2025-07-26T12:01:33.947Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cf/8f/5847f44a7fddf859704217a99a23a4f6417b10e5ab1256a179264561540e/contourpy-1.3.3-cp312-cp312-win32.whl", hash = "sha256:023b44101dfe49d7d53932be418477dba359649246075c996866106da069af69", size = 185018, upload-time = "2025-07-26T12:01:35.64Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/19/e8/6026ed58a64563186a9ee3f29f41261fd1828f527dd93d33b60feca63352/contourpy-1.3.3-cp312-cp312-win_amd64.whl", hash = "sha256:8153b8bfc11e1e4d75bcb0bff1db232f9e10b274e0929de9d608027e0d34ff8b", size = 226567, upload-time = "2025-07-26T12:01:36.804Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d1/e2/f05240d2c39a1ed228d8328a78b6f44cd695f7ef47beb3e684cf93604f86/contourpy-1.3.3-cp312-cp312-win_arm64.whl", hash = "sha256:07ce5ed73ecdc4a03ffe3e1b3e3c1166db35ae7584be76f65dbbe28a7791b0cc", size = 193655, upload-time = "2025-07-26T12:01:37.999Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a5/29/8dcfe16f0107943fa92388c23f6e05cff0ba58058c4c95b00280d4c75a14/contourpy-1.3.3-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:cd5dfcaeb10f7b7f9dc8941717c6c2ade08f587be2226222c12b25f0483ed497", size = 278809, upload-time = "2025-07-26T12:02:52.74Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/85/a9/8b37ef4f7dafeb335daee3c8254645ef5725be4d9c6aa70b50ec46ef2f7e/contourpy-1.3.3-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:0c1fc238306b35f246d61a1d416a627348b5cf0648648a031e14bb8705fcdfe8", size = 261593, upload-time = "2025-07-26T12:02:54.037Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0a/59/ebfb8c677c75605cc27f7122c90313fd2f375ff3c8d19a1694bda74aaa63/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70f9aad7de812d6541d29d2bbf8feb22ff7e1c299523db288004e3157ff4674e", size = 302202, upload-time = "2025-07-26T12:02:55.947Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3c/37/21972a15834d90bfbfb009b9d004779bd5a07a0ec0234e5ba8f64d5736f4/contourpy-1.3.3-pp311-pypy311_pp73-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:5ed3657edf08512fc3fe81b510e35c2012fbd3081d2e26160f27ca28affec989", size = 329207, upload-time = "2025-07-26T12:02:57.468Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0c/58/bd257695f39d05594ca4ad60df5bcb7e32247f9951fd09a9b8edb82d1daa/contourpy-1.3.3-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:3d1a3799d62d45c18bafd41c5fa05120b96a28079f2393af559b843d1a966a77", size = 225315, upload-time = "2025-07-26T12:02:58.801Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "courlan"
|
name = "courlan"
|
||||||
version = "1.4.0"
|
version = "1.4.0"
|
||||||
@@ -293,6 +350,62 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/1f/38/ce65091ff20a16e06d17418c4353af5f56d3190821b1a06983c79ae79274/courlan-1.4.0-py3-none-any.whl", hash = "sha256:ad1dbdefd912ca7238d4607dc855df5df097f56bac175dd662c84eed3802f49e", size = 34193, upload-time = "2026-06-01T17:30:14.984Z" },
|
{ url = "https://files.pythonhosted.org/packages/1f/38/ce65091ff20a16e06d17418c4353af5f56d3190821b1a06983c79ae79274/courlan-1.4.0-py3-none-any.whl", hash = "sha256:ad1dbdefd912ca7238d4607dc855df5df097f56bac175dd662c84eed3802f49e", size = 34193, upload-time = "2026-06-01T17:30:14.984Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cryptography"
|
||||||
|
version = "49.0.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "cffi", marker = "platform_python_implementation != 'PyPy'" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/1f/99/d1c90d6041656cc6ee229dc99cd67fd0cd5aec3c5f7d72fffc27cc750054/cryptography-49.0.0.tar.gz", hash = "sha256:f89660a348f4f78a92366240a61404e337586ef7f5909a2fef59ca88ef505493", size = 854345, upload-time = "2026-06-12T20:02:30.512Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9b/22/adf66990e63584a68dfb50c24f48a125c07b1699899381c8151e63ed458c/cryptography-49.0.0-cp311-abi3-macosx_11_0_arm64.whl", hash = "sha256:966fe0e9c67490071f14c0d2b1cb2dfb3023c5ce39457343931415f08382f2db", size = 4032100, upload-time = "2026-06-12T20:02:32.143Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/09/41/3797cfaf69cae04a13ee78ebd83f0678d9c02b4779d21ce24445326f1a69/cryptography-49.0.0-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:36d1709f992593689b45bda411498d62c6e365f2ca00b84657d4dadd24de16db", size = 4692978, upload-time = "2026-06-12T20:01:21.305Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e6/8b/43011f7ebe515a8aa20d61f290a326cd890c2e738e16e59eaff8d9c3a412/cryptography-49.0.0-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:0e959b578856a3924bc0cbb710fc12c387b9412a951389f3ca61704a9e25f325", size = 4716422, upload-time = "2026-06-12T20:01:48.566Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4a/91/01ce7303a4579e6d3a6abef01bd322848e9ea7a219adcabc5048b9033571/cryptography-49.0.0-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:53ecee2e23f7169b6117e99fc8a944e5e50f79e69758a83b52a00cb98ab2b2d2", size = 4700503, upload-time = "2026-06-12T20:02:47.091Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/62/99/a2c95cf8293f07491e9e27c20cc4dcd18176d944e674679adeb1d0173fd6/cryptography-49.0.0-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:2eda353d8a27bcbcaa4cbed18994a74ab4d19a2ca897db188ea269ab9b71419b", size = 5309779, upload-time = "2026-06-12T20:02:08.987Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/20/2c/0622f20ff02b2ef32558733443805dc82fd4c275be01b2d19d14676f3a1b/cryptography-49.0.0-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:2afe9051da7ae7bd5905da5a949280c7d2bb75682e188f650a9d0f2756b834c6", size = 4749683, upload-time = "2026-06-12T20:02:03.335Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a3/5b/c5246635d5fd3b64e0d45ae10e99fd32fe9676a79915ccfe5a61ba9af1a5/cryptography-49.0.0-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:0b82e28ee398a386f0807bba7884d30f25218855690f45115831bcce5d90822c", size = 4337874, upload-time = "2026-06-12T20:02:54.323Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6d/88/05563c7fe2e914e87d1a536d06fe83e66b4e1d95cb593e05aea375531da8/cryptography-49.0.0-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:ccac2bfebc306b862133e3bb71f3f6ee8bb525240089b2d952e4144b3a6d5da7", size = 4700283, upload-time = "2026-06-12T20:01:34.822Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c4/b6/d7696e4e890d6ae1469935164c9e5215c557671cb78d6e3f458ccceaa632/cryptography-49.0.0-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:d0527ce944105f257f605a827d6ebead966c752038b6e8656abb9c5edee6fc68", size = 5265844, upload-time = "2026-06-12T20:01:24.09Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a9/3c/f3ad17eecc1a57b0ba236dc01f90e783c51f4a2f35f64777cc4f47a184b2/cryptography-49.0.0-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:cbc77da8c523d5abd028635ba850a6966fcee2c82e2bf65a41d1d8afe0f98be9", size = 4749290, upload-time = "2026-06-12T20:01:30.848Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4f/01/339573cf1023163a400b0b5d16f6d507de413b9f60be6fd1b77feeaf6737/cryptography-49.0.0-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:b87e65d263b3e5d3bb92a57e2a6638e2f31110fa7aa890c7b2dbba42248d0a3f", size = 4834612, upload-time = "2026-06-12T20:01:29.246Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/71/fd/577302e213a1be9468f92d1afef66fcf1ef83d516819d9992ca547f592bd/cryptography-49.0.0-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:66ec79c3904820572d7e987abdf304281f141d37ad9a489b8e97066e7b9b6459", size = 4980804, upload-time = "2026-06-12T20:01:42.853Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1f/09/f42b1d190c5ba75f72062a387f8030d1d75f6ab035788f1d9c4b01de6525/cryptography-49.0.0-cp311-abi3-win_amd64.whl", hash = "sha256:e5dfc1e64de5677cec922ffa8da89c546d0415bf6efdf081842e5d44c84e1f0e", size = 3810026, upload-time = "2026-06-12T20:02:39.262Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/19/2a/5bb823f5bedcf80718cea7fbc95ec5515cca3769633c4b01a32be7f30e7c/cryptography-49.0.0-cp39-abi3-macosx_11_0_arm64.whl", hash = "sha256:ec5e529fb80935c94fe7b729f9972b50e351a0e6b50aa294fd5cabb109fcc29a", size = 4025947, upload-time = "2026-06-12T20:01:25.745Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3d/df/40577043ca124e17012f408ddddaeb213b856336ac82ddb3bc915f39e29f/cryptography-49.0.0-cp39-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:f78ff2c9ed8dc2d036b0f4d640e22522213d047c1b14e61205a7e55c80a494d4", size = 4692429, upload-time = "2026-06-12T20:01:53.628Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2c/99/2d13299eb3dd27b02dcfaafcc91d6b5cb3329f7cbd6d8f51921acd566c1a/cryptography-49.0.0-cp39-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:35b151772baff2c74cba7fa290ceaff4c3b11c0c881eb93eb5dbc05a7cfbba18", size = 4700968, upload-time = "2026-06-12T20:02:45.383Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a5/4d/9c0cd02f95e2602dd5e563da149ee0830abef3537be8b34dc56281ebe27a/cryptography-49.0.0-cp39-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:0f21641cf4b30fca7aee061ced0ec7ad7b073518088b7c9969a297c0ae796c69", size = 4697758, upload-time = "2026-06-12T20:01:41.13Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/24/01/186c825898477d77e2324d5360fefe622ff1d8d1963ec0554e2cada8ec77/cryptography-49.0.0-cp39-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:9e82dcc8e56052715fb18b2429e3bca4823b1629136a2084fc45a9a5cecb9b64", size = 5298863, upload-time = "2026-06-12T20:02:24.579Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b8/7b/62cbbab75d0659865bf0273790031544a0b16c8072d258f9428dcd8190dc/cryptography-49.0.0-cp39-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:6f2debedf9ca60cf1d5bd466475638af5130f89965605cd818484d19987d3a21", size = 4735983, upload-time = "2026-06-12T20:01:50.14Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6c/72/3e798c064bc39e471008075d0f9bc9daf77a80879c092e4a8e170c585ed4/cryptography-49.0.0-cp39-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:8c25ceb16df5b9435f3f6a9829204985b0e0cbee3b48aacd432c7d2c850b44d9", size = 4334173, upload-time = "2026-06-12T20:01:44.743Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f0/ee/6fca21d1ac73e06f8bef71940abfd4d2f6472b4bca284d770f32bd4086f6/cryptography-49.0.0-cp39-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:28d8b15e6275f12c8a207dc309dfa957903c927d08d0cc937ee3f63f200693cc", size = 4697298, upload-time = "2026-06-12T20:02:20.918Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/67/d0/a5fcd3515f0bae49a7b6d0413cc1bdccdcc1fc0047037a0d480642cdc5d6/cryptography-49.0.0-cp39-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:6fc361c34fb6aac015ce19435876635e5c6d21db31998b0920f675f131e043b8", size = 5254338, upload-time = "2026-06-12T20:02:22.737Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a0/84/84fe36f19caf857d61cb7fc9c63035a47ffabd84ea12d1d393148efa3615/cryptography-49.0.0-cp39-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:2400ef9c9e2299a25614eb1dea3db54a69b1349efd043bfac9c67630d136df36", size = 4735650, upload-time = "2026-06-12T20:02:41.389Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6c/a0/db537264e234f7273a73ec020873d6d6b39dfd8a53db78b550ca8320440e/cryptography-49.0.0-cp39-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:67e1d20ad9ef3a563c59ef22e7a8a0b8210bd26604369ea4a30a7c66aefe504e", size = 4834820, upload-time = "2026-06-12T20:01:51.847Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/93/77/8df9eb486495979bccecd1062e2eaf435250e84437040295b57d09048b0b/cryptography-49.0.0-cp39-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:42b0684e0e40cf26122427802486f6d93aea593612603a94fbf260c7eb1e9c1b", size = 4967968, upload-time = "2026-06-12T20:02:12.524Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c2/e6/f60198ea8d9dfa15fff9ed4ca02ce362f6eadd9ba757dcc50634c4257b63/cryptography-49.0.0-cp39-abi3-win_amd64.whl", hash = "sha256:026ac7423e6fa66872d3bf889be5974507da3944f866f704fa200eadacd00001", size = 3785547, upload-time = "2026-06-12T20:02:26.847Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/63/d3/4a83af35d65e3fad632c926fad684c193ea4398569ccb0bbbc7fe8f5dc9a/cryptography-49.0.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:fc1e275c2f1d97b1a6450b8b0ea3ebfa6e087a611c2b26cb2404d48588abab7b", size = 3993685, upload-time = "2026-06-12T20:02:14.883Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d6/a7/f9dac0ab7f80368c56993a7bf638ef9935f825c91902798481fac0898138/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_28_aarch64.whl", hash = "sha256:c83782480a4a9da4d0feb51950131ba32e12e70813848b3343f6e18c28a66838", size = 4676239, upload-time = "2026-06-12T20:02:28.793Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d7/70/2ba3769dd0ae167e2f33dfa9592d45db6ff9a61d62ca1a5b3d1bdd09068f/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_28_x86_64.whl", hash = "sha256:b39efa323140595abd3ecca8529d321ae50f55f3aa3ba9cc81ea56a6011953d5", size = 4715584, upload-time = "2026-06-12T20:01:27.495Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/94/64/2923570ac1c0bd3a737aa366ac3abbbbde273042308b8cde95e2364a6e6a/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_34_aarch64.whl", hash = "sha256:b47db11c2c3525083296069b98ac5221907455e989ae0c2e3008bde851921615", size = 4675885, upload-time = "2026-06-12T20:01:55.49Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ab/f8/614dc7e051418cfe53d55173c1e24c6b0085e89996fe90508c2fdf769aef/cryptography-49.0.0-pp311-pypy311_pp73-manylinux_2_34_x86_64.whl", hash = "sha256:084ef1af862eb07ec46d25f68689f2102a9fc0e05ce7b80f14f5fe51e4eef0f6", size = 4715449, upload-time = "2026-06-12T20:02:05.469Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/aa/50/a9caea39ad19c431c1a3f8a31114df65b260cdfe67786b6c7e7c040c4c44/cryptography-49.0.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:be9fcb48a55f023493482827d4f459bd263cc20efde64f204b97c123201850c6", size = 3783731, upload-time = "2026-06-12T20:02:43.319Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cssselect2"
|
||||||
|
version = "0.9.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "tinycss2" },
|
||||||
|
{ name = "webencodings" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/e0/20/92eaa6b0aec7189fa4b75c890640e076e9e793095721db69c5c81142c2e1/cssselect2-0.9.0.tar.gz", hash = "sha256:759aa22c216326356f65e62e791d66160a0f9c91d1424e8d8adc5e74dddfc6fb", size = 35595, upload-time = "2026-02-12T17:16:39.614Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/21/0e/8459ca4413e1a21a06c97d134bfaf18adfd27cea068813dc0faae06cbf00/cssselect2-0.9.0-py3-none-any.whl", hash = "sha256:6a99e5f91f9a016a304dd929b0966ca464bcfda15177b6fb4a118fc0fb5d9563", size = 15453, upload-time = "2026-02-12T17:16:38.317Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "curl-cffi"
|
name = "curl-cffi"
|
||||||
version = "0.15.0"
|
version = "0.15.0"
|
||||||
@@ -318,6 +431,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/19/6a/c24df8a4fc22fa84070dcd94abeba43c15e08cc09e35869565c0bad196fd/curl_cffi-0.15.0-cp313-abi3-android_24_arm64_v8a.whl", hash = "sha256:4682dc38d4336e0eb0b185374db90a760efde63cbea994b4e63f3521d44c4c92", size = 7190427, upload-time = "2026-04-03T11:12:12.142Z" },
|
{ url = "https://files.pythonhosted.org/packages/19/6a/c24df8a4fc22fa84070dcd94abeba43c15e08cc09e35869565c0bad196fd/curl_cffi-0.15.0-cp313-abi3-android_24_arm64_v8a.whl", hash = "sha256:4682dc38d4336e0eb0b185374db90a760efde63cbea994b4e63f3521d44c4c92", size = 7190427, upload-time = "2026-04-03T11:12:12.142Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "cycler"
|
||||||
|
version = "0.12.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/a9/95/a3dbbb5028f35eafb79008e7522a75244477d2838f38cbb722248dabc2a8/cycler-0.12.1.tar.gz", hash = "sha256:88bb128f02ba341da8ef447245a9e138fae777f6a23943da4540077d3601eb1c", size = 7615, upload-time = "2023-10-07T05:32:18.335Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e7/05/c19819d5e3d95294a6f5947fb9b9629efb316b96de511b418c53d245aae6/cycler-0.12.1-py3-none-any.whl", hash = "sha256:85cef7cff222d8644161529808465972e51340599459b8ac3ccbac5a854e0d30", size = 8321, upload-time = "2023-10-07T05:32:16.783Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "dateparser"
|
name = "dateparser"
|
||||||
version = "1.4.1"
|
version = "1.4.1"
|
||||||
@@ -379,6 +501,31 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/07/71/a190a7bd7ba86ed4a1dc2b938a23eca37a34464e409af15e3d46fa6d9cca/finnhub_python-2.4.29-py3-none-any.whl", hash = "sha256:5fbec39b40b9501d4165d94cb6fc7600017059d29719edbb01ae5c1ed13374c5", size = 12092, upload-time = "2026-06-24T07:14:14.138Z" },
|
{ url = "https://files.pythonhosted.org/packages/07/71/a190a7bd7ba86ed4a1dc2b938a23eca37a34464e409af15e3d46fa6d9cca/finnhub_python-2.4.29-py3-none-any.whl", hash = "sha256:5fbec39b40b9501d4165d94cb6fc7600017059d29719edbb01ae5c1ed13374c5", size = 12092, upload-time = "2026-06-24T07:14:14.138Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fonttools"
|
||||||
|
version = "4.63.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/84/69/c97f2c18e0db87d2c7b15da1974dace76ae938f1cfa22e2727a648b7ed43/fonttools-4.63.0.tar.gz", hash = "sha256:caeb583deeb5168e694b65cda8b4ee62abedfa66cf88488734466f2366b9c4e0", size = 3597189, upload-time = "2026-05-14T12:04:30.958Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/75/2b/a7f1545bdf5da69c4bda0cea2a5781f0ad2a6623e0277267672db43c5fe6/fonttools-4.63.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:2b8ae05d9eacf6081414d759c0a352769ac28ce31280d6bb8e77b03f9e3c449f", size = 2881793, upload-time = "2026-05-14T12:02:56.645Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/49/50/965308c703f085f225db2886813b27e015b8b3438c350b22dd65b52c2a2c/fonttools-4.63.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:79cdc9f567aec74a72918fd060283911406750cbc9fd28c1316023deb6ce31a9", size = 2428130, upload-time = "2026-05-14T12:02:58.891Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d8/38/6937fbd7f2dc3a6b48725851bc2c15ec949b9af14d9bbcb5fe83cdf9bdf9/fonttools-4.63.0-cp311-cp311-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:2c14b4fd138c4bafcca294765c547914e1aa431ae1ca94ab99d8db08c958bd3b", size = 5111952, upload-time = "2026-05-14T12:03:01.263Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0b/43/a81f20050a3115b57d62c8e781446949512eac36690dc384ccea65ff4cc1/fonttools-4.63.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:d76ac49f929aecaf82d83250b8347e099d7aecba0f4726c1d9b6df3b8bb5fe18", size = 5082308, upload-time = "2026-05-14T12:03:03.211Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/67/00/cdd9d4944ca6ae280d01e69cc37bde3bf663630b837a6fc6d2cd65d80e0e/fonttools-4.63.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:dcf076a4474fe0d7367e5bbf5b052c7284fa1feca729c04176ce513521afd8a0", size = 5087932, upload-time = "2026-05-14T12:03:05.147Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f5/f1/0aa0dbea778c75adbef223c42019fd47d22262b905974d62d829545d485f/fonttools-4.63.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:7dd683fef0663e9f0f45cf541d788d24caa3ec9db50796b588e1757d8b3bc007", size = 5213271, upload-time = "2026-05-14T12:03:07.238Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a8/99/253e4056e1f0e67b9390125a154b73b5eb73ad521bece95c004858fdeec2/fonttools-4.63.0-cp311-cp311-win32.whl", hash = "sha256:afefc1ed0a59785a7fb06ea7e1678e849c193e1e387db783579bc7b3056fcfcb", size = 2304473, upload-time = "2026-05-14T12:03:09.271Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/08/60/defa5e69641db890a63be281f41345f4c33b157824eaf0b9fad3e08b0dcb/fonttools-4.63.0-cp311-cp311-win_amd64.whl", hash = "sha256:063e08bd17bd5a90127a14123de0d6a952dbc847695fd98b63c043d58057f90c", size = 2356389, upload-time = "2026-05-14T12:03:11.53Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/08/ef/b3c6b9b5be2f82416d73fe2ed2e96e2793cd80e7510bd6a17ca79cdd88ec/fonttools-4.63.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:37dd23e621e3b0aef1baa70a303b80aaf38449632cfc8fd2a55fb285bbccfc02", size = 2881131, upload-time = "2026-05-14T12:03:13.386Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/44/a0/c815bea63117fa63e4e1c01f8a1110d2112fa003f838e6467094ec2432ce/fonttools-4.63.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:a9faff9e0c1f76f9fd55899d2ce785832efebab37eb8ae13995853aef178bef0", size = 2426704, upload-time = "2026-05-14T12:03:15.801Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/44/04/0b91d8e916e92ad1fac9e4624760baf0fd5ff2ead614c2f68fb21373f03f/fonttools-4.63.0-cp312-cp312-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ef3048ef05dbb552b89817713d9cac912e00d0fde4a3105c00d29e52e10c89af", size = 5044298, upload-time = "2026-05-14T12:03:18.085Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/77/c7/2342da9830e3e9d4870305ca5d2091d2a83284f2953079b7bdd3b5e029d8/fonttools-4.63.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:58dc6bb86a78d782f00f9190ca02c119cf5bbe2807536e361e18d42019f877d8", size = 4999800, upload-time = "2026-05-14T12:03:20.161Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e6/6d/67fe16c48d7ce050979b33f47e0d28a318f02da030602e944c34f7a16ef3/fonttools-4.63.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ee08ebfa58f6e1aeff5697ab9582105bb620008c1caafb681e4c557e7483027b", size = 4982666, upload-time = "2026-05-14T12:03:22.87Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f2/00/3bbab338c07c71fa56269953845e92c951a61457bbbb0f1022551ea266d9/fonttools-4.63.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:27fdc65af8da6f88b9c6121c47a464cbe359fcfff7ff6fc2d37a1f395d755b78", size = 5133598, upload-time = "2026-05-14T12:03:25.168Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/62/f2/aa27c7f98db5b064883dadcc5283947e81e034de42e22a33675878d98b54/fonttools-4.63.0-cp312-cp312-win32.whl", hash = "sha256:af2fd1664d00a397d75f806985ddb36282091c2131a73a6485c23b4a34722263", size = 2292575, upload-time = "2026-05-14T12:03:27.496Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/87/36/cccb9bc2a6ab63d1b2980374f0dca72ce95ae267c9b4cfe77455bb70d0d4/fonttools-4.63.0-cp312-cp312-win_amd64.whl", hash = "sha256:59ac449f8cca9b4ffa08d2e7bbadad87ce710d69d1eda5c3c1ce579baa987272", size = 2343211, upload-time = "2026-05-14T12:03:30.057Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2c/47/c99d5268f354002ce80f8d029cd9d7d872969da1de8b93d32de4dc56d6f4/fonttools-4.63.0-py3-none-any.whl", hash = "sha256:445af2eab030a16b9171ea8bdda7ebf7d96bda2df88ee182a464252f6e05e20d", size = 1164562, upload-time = "2026-05-14T12:04:29.092Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "gitdb"
|
name = "gitdb"
|
||||||
version = "4.0.12"
|
version = "4.0.12"
|
||||||
@@ -434,6 +581,19 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/71/b4/4a9fcfb2aef6ba44d9073ecd301443aa00b3dac95de5619f2a7de7ec8a91/hpack-4.2.0-py3-none-any.whl", hash = "sha256:858ac0b02280fa582b5080d68db0899c62a80375e0e5413a74970c5e518b6986", size = 34246, upload-time = "2026-06-23T18:34:45.472Z" },
|
{ url = "https://files.pythonhosted.org/packages/71/b4/4a9fcfb2aef6ba44d9073ecd301443aa00b3dac95de5619f2a7de7ec8a91/hpack-4.2.0-py3-none-any.whl", hash = "sha256:858ac0b02280fa582b5080d68db0899c62a80375e0e5413a74970c5e518b6986", size = 34246, upload-time = "2026-06-23T18:34:45.472Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "html5lib"
|
||||||
|
version = "1.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "six" },
|
||||||
|
{ name = "webencodings" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/ac/b6/b55c3f49042f1df3dcd422b7f224f939892ee94f22abcf503a9b7339eaf2/html5lib-1.1.tar.gz", hash = "sha256:b2e5b40261e20f354d198eae92afc10d750afb487ed5e50f9c4eaf07c184146f", size = 272215, upload-time = "2020-06-22T23:32:38.834Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6c/dd/a834df6482147d48e225a49515aabc28974ad5a4ca3215c18a882565b028/html5lib-1.1-py2.py3-none-any.whl", hash = "sha256:0d78f8fde1c230e99fe37986a60526d7049ed4bf8a9fadbad5f00e22e58e041d", size = 112173, upload-time = "2020-06-22T23:32:36.781Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "htmldate"
|
name = "htmldate"
|
||||||
version = "1.10.0"
|
version = "1.10.0"
|
||||||
@@ -664,6 +824,53 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/f2/ac/52f4e86d1924a7fc05af3aeb34488570eccc39b4af90530dd6acecdf16b5/justext-3.0.2-py2.py3-none-any.whl", hash = "sha256:62b1c562b15c3c6265e121cc070874243a443bfd53060e869393f09d6b6cc9a7", size = 837940, upload-time = "2025-02-25T20:21:44.179Z" },
|
{ url = "https://files.pythonhosted.org/packages/f2/ac/52f4e86d1924a7fc05af3aeb34488570eccc39b4af90530dd6acecdf16b5/justext-3.0.2-py2.py3-none-any.whl", hash = "sha256:62b1c562b15c3c6265e121cc070874243a443bfd53060e869393f09d6b6cc9a7", size = 837940, upload-time = "2025-02-25T20:21:44.179Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "kiwisolver"
|
||||||
|
version = "1.5.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d0/67/9c61eccb13f0bdca9307614e782fec49ffdde0f7a2314935d489fa93cd9c/kiwisolver-1.5.0.tar.gz", hash = "sha256:d4193f3d9dc3f6f79aaed0e5637f45d98850ebf01f7ca20e69457f3e8946b66a", size = 103482, upload-time = "2026-03-09T13:15:53.382Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/12/dd/a495a9c104be1c476f0386e714252caf2b7eca883915422a64c50b88c6f5/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_universal2.whl", hash = "sha256:9eed0f7edbb274413b6ee781cca50541c8c0facd3d6fd289779e494340a2b85c", size = 122798, upload-time = "2026-03-09T13:12:58.963Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/11/60/37b4047a2af0cf5ef6d8b4b26e91829ae6fc6a2d1f74524bcb0e7cd28a32/kiwisolver-1.5.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:3c4923e404d6bcd91b6779c009542e5647fef32e4a5d75e115e3bbac6f2335eb", size = 66216, upload-time = "2026-03-09T13:13:00.155Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0a/aa/510dc933d87767584abfe03efa445889996c70c2990f6f87c3ebaa0a18c5/kiwisolver-1.5.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:0df54df7e686afa55e6f21fb86195224a6d9beb71d637e8d7920c95cf0f89aac", size = 63911, upload-time = "2026-03-09T13:13:01.671Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/80/46/bddc13df6c2a40741e0cc7865bb1c9ed4796b6760bd04ce5fae3928ef917/kiwisolver-1.5.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:2517e24d7315eb51c10664cdb865195df38ab74456c677df67bb47f12d088a27", size = 1438209, upload-time = "2026-03-09T13:13:03.385Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fd/d6/76621246f5165e5372f02f5e6f3f48ea336a8f9e96e43997d45b240ed8cd/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:ff710414307fefa903e0d9bdf300972f892c23477829f49504e59834f4195398", size = 1248888, upload-time = "2026-03-09T13:13:05.231Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b2/c1/31559ec6fb39a5b48035ce29bb63ade628f321785f38c384dee3e2c08bc1/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:6176c1811d9d5a04fa391c490cc44f451e240697a16977f11c6f722efb9041db", size = 1266304, upload-time = "2026-03-09T13:13:06.743Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5e/ef/1cb8276f2d29cc6a41e0a042f27946ca347d3a4a75acf85d0a16aa6dcc82/kiwisolver-1.5.0-cp311-cp311-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50847dca5d197fcbd389c805aa1a1cf32f25d2e7273dc47ab181a517666b68cc", size = 1319650, upload-time = "2026-03-09T13:13:08.607Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4c/e4/5ba3cecd7ce6236ae4a80f67e5d5531287337d0e1f076ca87a5abe4cd5d0/kiwisolver-1.5.0-cp311-cp311-manylinux_2_39_riscv64.whl", hash = "sha256:01808c6d15f4c3e8559595d6d1fe6411c68e4a3822b4b9972b44473b24f4e679", size = 970949, upload-time = "2026-03-09T13:13:10.299Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5a/69/dc61f7ae9a2f071f26004ced87f078235b5507ab6e5acd78f40365655034/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:f1f9f4121ec58628c96baa3de1a55a4e3a333c5102c8e94b64e23bf7b2083309", size = 2199125, upload-time = "2026-03-09T13:13:11.841Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e5/7b/abbe0f1b5afa85f8d084b73e90e5f801c0939eba16ac2e49af7c61a6c28d/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_ppc64le.whl", hash = "sha256:b7d335370ae48a780c6e6a6bbfa97342f563744c39c35562f3f367665f5c1de2", size = 2293783, upload-time = "2026-03-09T13:13:14.399Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8a/80/5908ae149d96d81580d604c7f8aefd0e98f4fd728cf172f477e9f2a81744/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_riscv64.whl", hash = "sha256:800ee55980c18545af444d93fdd60c56b580db5cc54867d8cbf8a1dc0829938c", size = 1960726, upload-time = "2026-03-09T13:13:16.047Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/84/08/a78cb776f8c085b7143142ce479859cfec086bd09ee638a317040b6ef420/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_s390x.whl", hash = "sha256:c438f6ca858697c9ab67eb28246c92508af972e114cac34e57a6d4ba17a3ac08", size = 2464738, upload-time = "2026-03-09T13:13:17.897Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b1/e1/65584da5356ed6cb12c63791a10b208860ac40a83de165cb6a6751a686e3/kiwisolver-1.5.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:8c63c91f95173f9c2a67c7c526b2cea976828a0e7fced9cdcead2802dc10f8a4", size = 2270718, upload-time = "2026-03-09T13:13:19.421Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/be/6c/28f17390b62b8f2f520e2915095b3c94d88681ecf0041e75389d9667f202/kiwisolver-1.5.0-cp311-cp311-win_amd64.whl", hash = "sha256:beb7f344487cdcb9e1efe4b7a29681b74d34c08f0043a327a74da852a6749e7b", size = 73480, upload-time = "2026-03-09T13:13:20.818Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d8/0e/2ee5debc4f77a625778fec5501ff3e8036fe361b7ee28ae402a485bb9694/kiwisolver-1.5.0-cp311-cp311-win_arm64.whl", hash = "sha256:ad4ae4ffd1ee9cd11357b4c66b612da9888f4f4daf2f36995eda64bd45370cac", size = 64930, upload-time = "2026-03-09T13:13:21.997Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4d/b2/818b74ebea34dabe6d0c51cb1c572e046730e64844da6ed646d5298c40ce/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_universal2.whl", hash = "sha256:4e9750bc21b886308024f8a54ccb9a2cc38ac9fa813bf4348434e3d54f337ff9", size = 123158, upload-time = "2026-03-09T13:13:23.127Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bf/d9/405320f8077e8e1c5c4bd6adc45e1e6edf6d727b6da7f2e2533cf58bff71/kiwisolver-1.5.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:72ec46b7eba5b395e0a7b63025490d3214c11013f4aacb4f5e8d6c3041829588", size = 66388, upload-time = "2026-03-09T13:13:24.765Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/99/9f/795fedf35634f746151ca8839d05681ceb6287fbed6cc1c9bf235f7887c2/kiwisolver-1.5.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:ed3a984b31da7481b103f68776f7128a89ef26ed40f4dc41a2223cda7fb24819", size = 64068, upload-time = "2026-03-09T13:13:25.878Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c4/13/680c54afe3e65767bed7ec1a15571e1a2f1257128733851ade24abcefbcc/kiwisolver-1.5.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:bb5136fb5352d3f422df33f0c879a1b0c204004324150cc3b5e3c4f310c9049f", size = 1477934, upload-time = "2026-03-09T13:13:27.166Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c8/2f/cebfcdb60fd6a9b0f6b47a9337198bcbad6fbe15e68189b7011fd914911f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b2af221f268f5af85e776a73d62b0845fc8baf8ef0abfae79d29c77d0e776aaf", size = 1278537, upload-time = "2026-03-09T13:13:28.707Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f2/0d/9b782923aada3fafb1d6b84e13121954515c669b18af0c26e7d21f579855/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b0f172dc8ffaccb8522d7c5d899de00133f2f1ca7b0a49b7da98e901de87bf2d", size = 1296685, upload-time = "2026-03-09T13:13:30.528Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/27/70/83241b6634b04fe44e892688d5208332bde130f38e610c0418f9ede47ded/kiwisolver-1.5.0-cp312-cp312-manylinux_2_24_s390x.manylinux_2_28_s390x.whl", hash = "sha256:6ab8ba9152203feec73758dad83af9a0bbe05001eb4639e547207c40cfb52083", size = 1346024, upload-time = "2026-03-09T13:13:32.818Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e4/db/30ed226fb271ae1a6431fc0fe0edffb2efe23cadb01e798caeb9f2ceae8f/kiwisolver-1.5.0-cp312-cp312-manylinux_2_39_riscv64.whl", hash = "sha256:cdee07c4d7f6d72008d3f73b9bf027f4e11550224c7c50d8df1ae4a37c1402a6", size = 987241, upload-time = "2026-03-09T13:13:34.435Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ec/bd/c314595208e4c9587652d50959ead9e461995389664e490f4dce7ff0f782/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:7c60d3c9b06fb23bd9c6139281ccbdc384297579ae037f08ae90c69f6845c0b1", size = 2227742, upload-time = "2026-03-09T13:13:36.4Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c1/43/0499cec932d935229b5543d073c2b87c9c22846aab48881e9d8d6e742a2d/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_ppc64le.whl", hash = "sha256:e315e5ec90d88e140f57696ff85b484ff68bb311e36f2c414aa4286293e6dee0", size = 2323966, upload-time = "2026-03-09T13:13:38.204Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3d/6f/79b0d760907965acfd9d61826a3d41f8f093c538f55cd2633d3f0db269f6/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_riscv64.whl", hash = "sha256:1465387ac63576c3e125e5337a6892b9e99e0627d52317f3ca79e6930d889d15", size = 1977417, upload-time = "2026-03-09T13:13:39.966Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ab/31/01d0537c41cb75a551a438c3c7a80d0c60d60b81f694dac83dd436aec0d0/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_s390x.whl", hash = "sha256:530a3fd64c87cffa844d4b6b9768774763d9caa299e9b75d8eca6a4423b31314", size = 2491238, upload-time = "2026-03-09T13:13:41.698Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e4/34/8aefdd0be9cfd00a44509251ba864f5caf2991e36772e61c408007e7f417/kiwisolver-1.5.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:1d9daea4ea6b9be74fe2f01f7fbade8d6ffab263e781274cffca0dba9be9eec9", size = 2294947, upload-time = "2026-03-09T13:13:43.343Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ad/cf/0348374369ca588f8fe9c338fae49fa4e16eeb10ffb3d012f23a54578a9e/kiwisolver-1.5.0-cp312-cp312-win_amd64.whl", hash = "sha256:f18c2d9782259a6dc132fdc7a63c168cbc74b35284b6d75c673958982a378384", size = 73569, upload-time = "2026-03-09T13:13:45.792Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/28/26/192b26196e2316e2bd29deef67e37cdf9870d9af8e085e521afff0fed526/kiwisolver-1.5.0-cp312-cp312-win_arm64.whl", hash = "sha256:f7c7553b13f69c1b29a5bde08ddc6d9d0c8bfb84f9ed01c30db25944aeb852a7", size = 64997, upload-time = "2026-03-09T13:13:46.878Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/1c/fa/2910df836372d8761bb6eff7d8bdcb1613b5c2e03f260efe7abe34d388a7/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_10_13_x86_64.whl", hash = "sha256:5ae8e62c147495b01a0f4765c878e9bfdf843412446a247e28df59936e99e797", size = 130262, upload-time = "2026-03-09T13:15:35.629Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0f/41/c5f71f9f00aabcc71fee8b7475e3f64747282580c2fe748961ba29b18385/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-macosx_11_0_arm64.whl", hash = "sha256:f6764a4ccab3078db14a632420930f6186058750df066b8ea2a7106df91d3203", size = 138036, upload-time = "2026-03-09T13:15:36.894Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fa/06/7399a607f434119c6e1fdc8ec89a8d51ccccadf3341dee4ead6bd14caaf5/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-manylinux_2_24_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c31c13da98624f957b0fb1b5bae5383b2333c2c3f6793d9825dd5ce79b525cb7", size = 194295, upload-time = "2026-03-09T13:15:38.22Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b5/91/53255615acd2a1eaca307ede3c90eb550bae9c94581f8c00081b6b1c8f44/kiwisolver-1.5.0-graalpy312-graalpy250_312_native-win_amd64.whl", hash = "sha256:1f1489f769582498610e015a8ef2d36f28f505ab3096d0e16b4858a9ec214f57", size = 75987, upload-time = "2026-03-09T13:15:39.65Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e9/eb/5fcbbbf9a0e2c3a35effb88831a483345326bbc3a030a3b5b69aee647f84/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:ec4c85dc4b687c7f7f15f553ff26a98bfe8c58f5f7f0ac8905f0ba4c7be60232", size = 59532, upload-time = "2026-03-09T13:15:47.047Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c3/9b/e17104555bb4db148fd52327feea1e96be4b88e8e008b029002c281a21ab/kiwisolver-1.5.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:12e91c215a96e39f57989c8912ae761286ac5a9584d04030ceb3368a357f017a", size = 57420, upload-time = "2026-03-09T13:15:48.199Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/48/44/2b5b95b7aa39fb2d8d9d956e0f3d5d45aef2ae1d942d4c3ffac2f9cfed1a/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be4a51a55833dc29ab5d7503e7bcb3b3af3402d266018137127450005cdfe737", size = 79892, upload-time = "2026-03-09T13:15:49.694Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/52/7d/7157f9bba6b455cfb4632ed411e199fc8b8977642c2b12082e1bd9e6d173/kiwisolver-1.5.0-pp311-pypy311_pp73-manylinux_2_24_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:daae526907e262de627d8f70058a0f64acc9e2641c164c99c8f594b34a799a16", size = 77603, upload-time = "2026-03-09T13:15:50.945Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0a/dd/8050c947d435c8d4bc94e3252f4d8bb8a76cfb424f043a8680be637a57f1/kiwisolver-1.5.0-pp311-pypy311_pp73-win_amd64.whl", hash = "sha256:59cd8683f575d96df5bb48f6add94afc055012c29e28124fcae2b63661b9efb1", size = 73558, upload-time = "2026-03-09T13:15:52.112Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "langchain-core"
|
name = "langchain-core"
|
||||||
version = "1.4.8"
|
version = "1.4.8"
|
||||||
@@ -886,6 +1093,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/6a/bd/6e2b76a6c5dee10397db9c929f0c5066766ec1036046f0335b7ca7ca08b8/lxml_html_clean-0.4.5-py3-none-any.whl", hash = "sha256:c76fcadd1e5bfb9b8bafc2200d51e4e78eb0dad67f56881c21dfb6484c7e7746", size = 14573, upload-time = "2026-05-20T12:17:52.215Z" },
|
{ url = "https://files.pythonhosted.org/packages/6a/bd/6e2b76a6c5dee10397db9c929f0c5066766ec1036046f0335b7ca7ca08b8/lxml_html_clean-0.4.5-py3-none-any.whl", hash = "sha256:c76fcadd1e5bfb9b8bafc2200d51e4e78eb0dad67f56881c21dfb6484c7e7746", size = 14573, upload-time = "2026-05-20T12:17:52.215Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "markdown"
|
||||||
|
version = "3.10.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/2b/f4/69fa6ed85ae003c2378ffa8f6d2e3234662abd02c10d216c0ba96081a238/markdown-3.10.2.tar.gz", hash = "sha256:994d51325d25ad8aa7ce4ebaec003febcce822c3f8c911e3b17c52f7f589f950", size = 368805, upload-time = "2026-02-09T14:57:26.942Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/de/1f/77fa3081e4f66ca3576c896ae5d31c3002ac6607f9747d2e3aa49227e464/markdown-3.10.2-py3-none-any.whl", hash = "sha256:e91464b71ae3ee7afd3017d9f358ef0baf158fd9a298db92f1d4761133824c36", size = 108180, upload-time = "2026-02-09T14:57:25.787Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "markdown-it-py"
|
name = "markdown-it-py"
|
||||||
version = "4.2.0"
|
version = "4.2.0"
|
||||||
@@ -928,6 +1144,43 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" },
|
{ url = "https://files.pythonhosted.org/packages/e5/f1/216fc1bbfd74011693a4fd837e7026152e89c4bcf3e77b6692fba9923123/markupsafe-3.0.3-cp312-cp312-win_arm64.whl", hash = "sha256:35add3b638a5d900e807944a078b51922212fb3dedb01633a8defc4b01a3c85f", size = 13906, upload-time = "2025-09-27T18:36:40.689Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "matplotlib"
|
||||||
|
version = "3.11.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "contourpy" },
|
||||||
|
{ name = "cycler" },
|
||||||
|
{ name = "fonttools" },
|
||||||
|
{ name = "kiwisolver" },
|
||||||
|
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
|
||||||
|
{ name = "numpy", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
|
||||||
|
{ name = "packaging" },
|
||||||
|
{ name = "pillow" },
|
||||||
|
{ name = "pyparsing" },
|
||||||
|
{ name = "python-dateutil" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/1f/24/080c99d223d158d3a8902769269ab6da5b50f7a0e6e072513907e02b7a6c/matplotlib-3.11.0.tar.gz", hash = "sha256:68c0c7be01b30dcca3638934f7f591df73401235cbdbf0d1ab1c71e7db7f8b57", size = 33251176, upload-time = "2026-06-12T02:29:15.508Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ce/a2/78f662f1b18968531f67d3fcde1b7ea8496920bacd4f16ddb5b79d112e46/matplotlib-3.11.0-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:f857524b442f0f36e641868ce2171aafa88cb0bc0644f4e1d8a5df9b32649fef", size = 9436261, upload-time = "2026-06-12T02:27:34.161Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/5e/92/044f1de43901310202f4c79acf4f141be53b2ca8d8380e2fcefb3d523a75/matplotlib-3.11.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:57baa92fdc82948ed716eae6d2579d4d6f40965cd8d2f416755b4a72580a3233", size = 9264669, upload-time = "2026-06-12T02:27:37.413Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/53/f4/f0b4f9ba7ec14a7af8151f3ad71ecfe3561e6ba38cfab1db3681ba4ca112/matplotlib-3.11.0-cp311-cp311-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:630eee0e67d35cce2019a0e670719f4816e3b86aff0fa72729f6c69786fceb45", size = 10021076, upload-time = "2026-06-12T02:27:39.926Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d7/33/4d679c6dcd594a156542080ac907ddccf7b09ca11655c4b28eca8e9ee5da/matplotlib-3.11.0-cp311-cp311-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:5106c444d0bf966eee2853548c03772af4ab7199118e086c62fbac8ccb07c055", size = 10828999, upload-time = "2026-06-12T02:27:42.433Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/07/74/0a3683802037d8cd013144d77c247219b47f2aabace6fdde74faa12bacf7/matplotlib-3.11.0-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4d7aea652b58e686444079be3376ef546bffa1eee9b9bb9c472b9fcf6cf410d3", size = 10913103, upload-time = "2026-06-12T02:27:44.827Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d0/9f/970fcbf381e82ec66fdf5da8ea76e2e9240f61a24011ce9fd1d42c37ac2d/matplotlib-3.11.0-cp311-cp311-win_amd64.whl", hash = "sha256:70a5b3e9a5dab708c0f039709ae7c68d5b4d254e291ef76492cdba230c8bb5e4", size = 9310945, upload-time = "2026-06-12T02:27:46.867Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/14/4e/6e7cfed23611265ded53806852343b5c59339e506e84c474a9b5afc3b249/matplotlib-3.11.0-cp311-cp311-win_arm64.whl", hash = "sha256:3d68266213e73823ac3be90615bab0cf31f88851e114cdb1dd25dacf3b01e1a7", size = 8999304, upload-time = "2026-06-12T02:27:48.798Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/da/17/f5276b496c61477a6c4fc5e7401f4bfe1c2e5ef7c6cd67896f2ade3809cb/matplotlib-3.11.0-cp312-cp312-macosx_10_13_x86_64.whl", hash = "sha256:06b5872e9cf11adc8f589ded3ce11bc3e1061ad498259664fabc1f6615beb918", size = 9449976, upload-time = "2026-06-12T02:27:50.989Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/82/34/bdd77418adb2178a1d59f044bd67bfebb115896e91b840b8a197eb3f4f4e/matplotlib-3.11.0-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:0515d495124be3124340e59f164d901ed4484e2246a5b74cfa483cac3b80bd97", size = 9279307, upload-time = "2026-06-12T02:27:53.247Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/94/95/7f522393c88313336b20d70fc849555757b2e5febc22b83b3a3f0fd4bce9/matplotlib-3.11.0-cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:be5f93a1d21981bfb802ded0d77a0caa92d4342a47d45754fac77e314a506344", size = 10031353, upload-time = "2026-06-12T02:27:55.215Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/87/ce/8f25a0e3186aefd61913e7467d1b999465bcd0d0c03ac695c1b26ca559b7/matplotlib-3.11.0-cp312-cp312-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:41635d7909d19e52e924a521dde6d8f670b0f53ab1d0e8c331fa831554f681d1", size = 10839232, upload-time = "2026-06-12T02:27:57.746Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/85/c2/db15da2bbdf9e3ca66df7db8e2c33a1dfed67be24a24d2c878efaaff01d6/matplotlib-3.11.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:94f5000f67ca9faa300863ea17f8bce9175cb67b88bec4bc7780502d53dd7c9e", size = 10923899, upload-time = "2026-06-12T02:28:00.223Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e5/2f/a58a4443a4d052a4ea77557478336aefc26c7981f6408d37adba763aa758/matplotlib-3.11.0-cp312-cp312-win_amd64.whl", hash = "sha256:ac6f1ef39f3d0f9e2463303013094992cdbe0f85f43bc54155bc472b2042768e", size = 9329528, upload-time = "2026-06-12T02:28:02.27Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/61/0f/4b669589d47733b97ab9df4b58d6fc1e68acb5ea42a928dc7cbdd6bf5871/matplotlib-3.11.0-cp312-cp312-win_arm64.whl", hash = "sha256:9dd11fb612ce7bc60b1de5b4fc87ff959d22317b5de42aabf392f66f97af22eb", size = 9003413, upload-time = "2026-06-12T02:28:04.49Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0f/c2/f5da6cd37ed6871f5c9b3c0507ddb69f14d6c36fac4541e4e0c60cb8cdfc/matplotlib-3.11.0-pp311-pypy311_pp73-macosx_10_15_x86_64.whl", hash = "sha256:81ae77077a1e16d37a5b61096ccb07c8d90a99b518fa8256b8f21578932f2f62", size = 9434094, upload-time = "2026-06-12T02:29:09.135Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f8/07/56f66906e0f87a0c6d0d0acbd34dbc9432b1931d8f26ef618bd6f92932a9/matplotlib-3.11.0-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:ddef37840695f5eef65f9f070fe2d2f510f584c2156203f9f622a5b0584efffd", size = 9262183, upload-time = "2026-06-12T02:29:11.283Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/0c/d8/c4ecab06b7ea36a570c4f3bd2d48d1799fd5d9174470e45c2194199431e7/matplotlib-3.11.0-pp311-pypy311_pp73-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:cf662e5ac5707658cb931e19972c4bd99f7b4f8b7bf79d3c821d239fa6b71e64", size = 10015653, upload-time = "2026-06-12T02:29:13.251Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "mdurl"
|
name = "mdurl"
|
||||||
version = "0.1.2"
|
version = "0.1.2"
|
||||||
@@ -1134,6 +1387,18 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/c8/9e/029e898298b2cc662f10d7a15652a53e3b525b1e7f07e21fef8536a09bb8/ormsgpack-1.12.2-cp312-cp312-win_arm64.whl", hash = "sha256:da538c542bac7d1c8f3f2a937863dba36f013108ce63e55745941dda4b75dbb6", size = 111559, upload-time = "2026-01-18T20:55:54.273Z" },
|
{ url = "https://files.pythonhosted.org/packages/c8/9e/029e898298b2cc662f10d7a15652a53e3b525b1e7f07e21fef8536a09bb8/ormsgpack-1.12.2-cp312-cp312-win_arm64.whl", hash = "sha256:da538c542bac7d1c8f3f2a937863dba36f013108ce63e55745941dda4b75dbb6", size = 111559, upload-time = "2026-01-18T20:55:54.273Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "oscrypto"
|
||||||
|
version = "1.3.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "asn1crypto" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/06/81/a7654e654a4b30eda06ef9ad8c1b45d1534bfd10b5c045d0c0f6b16fecd2/oscrypto-1.3.0.tar.gz", hash = "sha256:6f5fef59cb5b3708321db7cca56aed8ad7e662853351e7991fcf60ec606d47a4", size = 184590, upload-time = "2022-03-18T01:53:26.889Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/01/7c/fa07d3da2b6253eb8474be16eab2eadf670460e364ccc895ca7ff388ee30/oscrypto-1.3.0-py2.py3-none-any.whl", hash = "sha256:2b2f1d2d42ec152ca90ccb5682f3e051fb55986e1b170ebde472b133713e7085", size = 194553, upload-time = "2022-03-18T01:53:24.559Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "packaging"
|
name = "packaging"
|
||||||
version = "26.2"
|
version = "26.2"
|
||||||
@@ -1415,6 +1680,58 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
|
{ url = "https://files.pythonhosted.org/packages/f4/7e/a72dd26f3b0f4f2bf1dd8923c85f7ceb43172af56d63c7383eb62b332364/pygments-2.20.0-py3-none-any.whl", hash = "sha256:81a9e26dd42fd28a23a2d169d86d7ac03b46e2f8b59ed4698fb4785f946d0176", size = 1231151, upload-time = "2026-03-29T13:29:30.038Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyhanko"
|
||||||
|
version = "0.35.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "asn1crypto" },
|
||||||
|
{ name = "cryptography" },
|
||||||
|
{ name = "lxml" },
|
||||||
|
{ name = "pyhanko-certvalidator" },
|
||||||
|
{ name = "pyyaml" },
|
||||||
|
{ name = "requests" },
|
||||||
|
{ name = "tzlocal" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/dd/5c/40bffd713756a64af554c45915325431b643238641ca1c2e81ddf27805dd/pyhanko-0.35.1.tar.gz", hash = "sha256:a6319f6f3753bee92ec0e9b91f638d37c58f8e49d494ef2c2a1f5bcf614707a6", size = 7674200, upload-time = "2026-05-06T00:05:38.854Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4a/1a/bea014b55c45c98c63dffd34d2dd01eed0a73ce5b175bd7b1ba798d13558/pyhanko-0.35.1-py3-none-any.whl", hash = "sha256:57c085f29d7f87334be2a5fbd38a1ca3c987f40f51aea38bde628497d6d9ef2e", size = 466983, upload-time = "2026-05-06T00:05:36.849Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyhanko-certvalidator"
|
||||||
|
version = "0.31.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "asn1crypto" },
|
||||||
|
{ name = "cryptography" },
|
||||||
|
{ name = "oscrypto" },
|
||||||
|
{ name = "requests" },
|
||||||
|
{ name = "uritools" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/2d/8b/93ee7af674045965e18cc53357bc338d24d2ca47bb71a750c0e38d1cc462/pyhanko_certvalidator-0.31.1.tar.gz", hash = "sha256:b7abc5672878994a050cf70a19b8247891aa30c9e6507eea77a6b10c7dbc1e50", size = 584700, upload-time = "2026-05-06T00:00:51.779Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f4/99/722e90c586455733fa860d092722ef074f3a65449fe9787b7b7e2dafcd1f/pyhanko_certvalidator-0.31.1-py3-none-any.whl", hash = "sha256:0aabe25b536ed555f3b18e5eb2c62ff46adeff9d15e27f0c9d5d81952d956f45", size = 111438, upload-time = "2026-05-06T00:00:50.198Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pyparsing"
|
||||||
|
version = "3.3.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/f3/91/9c6ee907786a473bf81c5f53cf703ba0957b23ab84c264080fb5a450416f/pyparsing-3.3.2.tar.gz", hash = "sha256:c777f4d763f140633dcb6d8a3eda953bf7a214dc4eff598413c070bcdc117cbc", size = 6851574, upload-time = "2026-01-21T03:57:59.36Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/10/bd/c038d7cc38edc1aa5bf91ab8068b63d4308c66c4c8bb3cbba7dfbc049f9c/pyparsing-3.3.2-py3-none-any.whl", hash = "sha256:850ba148bd908d7e2411587e247a1e4f0327839c40e2e5e6d05a007ecc69911d", size = 122781, upload-time = "2026-01-21T03:57:55.912Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "pypdf"
|
||||||
|
version = "6.14.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/03/72/7dfd5ff1c9c37de97a731701f51af091325f123d9d4270361c9c69e4431f/pypdf-6.14.2.tar.gz", hash = "sha256:7873f502fe4385e79539b21d872392dc0c4e3714327c15881cbc7fbfd1f95b25", size = 6491182, upload-time = "2026-06-23T14:18:30.859Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/49/e6/136aa8993a2ae7214e0b0ef2edaa0d2e08d1d4e4982635b08a835ff31ec8/pypdf-6.14.2-py3-none-any.whl", hash = "sha256:3f07891af76dc002657e04993ab9b4de81de29f9013b9761d0b7968bff12e946", size = 349514, upload-time = "2026-06-23T14:18:28.867Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "pytest"
|
name = "pytest"
|
||||||
version = "9.1.1"
|
version = "9.1.1"
|
||||||
@@ -1431,6 +1748,54 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" },
|
{ url = "https://files.pythonhosted.org/packages/24/25/1de2678b631f5a49215c6c96fff41ba892b0a34df68d6d80292b1b48aa7f/pytest-9.1.1-py3-none-any.whl", hash = "sha256:37a86b45efb9a47a61a36449063e8e18d0cab3161329fc099eb21783169c4f0c", size = 386536, upload-time = "2026-06-19T10:58:31.347Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "python-bidi"
|
||||||
|
version = "0.6.11"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/ce/e7/f168f2c3151aa05b9f9c9b2f7767bc8e06a133ea822c231ab497d4f36833/python_bidi-0.6.11.tar.gz", hash = "sha256:034090c597af250d699299d7e7f1e83eb016f9e47b3b707bd89ab2bdec77bce0", size = 57647, upload-time = "2026-06-30T14:23:42.626Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/71/4b/7f942cdb3cc948a369bfe2530343f2d650aa17bb04b0b959834919f699a4/python_bidi-0.6.11-cp311-cp311-macosx_10_12_x86_64.whl", hash = "sha256:56f27c1edfd15c12c9c348378ccd79166930d720cf316b1181a0a0ade2146253", size = 269449, upload-time = "2026-06-30T14:22:42.207Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ee/6e/af3e17cb48b87176c209ac4271c8a9aaad8c33f5535739b58336222e69af/python_bidi-0.6.11-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:a52f7ad9ef9091e81869e5d255e796755ccf542ade14dda17647cb7d7ffe1b9c", size = 269840, upload-time = "2026-06-30T14:22:31.833Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/91/62/f7303a11e8286b2219088bb863974398a1a9f117444e78bbfcefaef7bc14/python_bidi-0.6.11-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e4ebc24ac38e50676f65daf7ba6c568789660cb60d6dcf2606d4310dba826721", size = 295322, upload-time = "2026-06-30T14:21:29.37Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/29/38/930e63c374133760f69159da36a7aba98368ade44ae708215addc4079d91/python_bidi-0.6.11-cp311-cp311-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:969ee7db3e169fcc0b2d2d094826e03cc5798dfd6b3571a340ea883672396cb1", size = 300777, upload-time = "2026-06-30T14:21:40.302Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8d/af/f92408a2882ed7c94c3df9038df48400a5adc345e963d488bf716ad25358/python_bidi-0.6.11-cp311-cp311-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0d496f9fc21b7457e12395e54088ab99776966c663b4cd4a74770c7a6418ab59", size = 419532, upload-time = "2026-06-30T14:21:50.531Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/24/0c/5fb11159f50e9a898862fa40fe98b8994eccd224189bb1c805ecfae66977/python_bidi-0.6.11-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:ad5d9b8e8a6c330208eba413db506de58f21dbf88a1f1d5d75ef5f9e0e714adf", size = 320985, upload-time = "2026-06-30T14:22:01.157Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/16/d3/6bd8b189219ec128263b7277b1cdaed0cf014199c61e05793be2d9cb0456/python_bidi-0.6.11-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:acccb6d90e694684db2d314db2e2b0d3b8949bf1cfaec6d9808a8889f548add7", size = 299186, upload-time = "2026-06-30T14:22:21.702Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/36/9e/b7da9c128f5ed867a62cb8112602bc7a6b6af783bbac18ca303a8a09b868/python_bidi-0.6.11-cp311-cp311-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:a1b5ee069001bf7f4fff109598a9396caa96bb35e1b906b2c6d1bab9f9b2c4bd", size = 316856, upload-time = "2026-06-30T14:22:11.328Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/72/a9/f5e4c286ae22fff569eb7aeeec7a5342e26e0ed1ac8202c48cf273932b28/python_bidi-0.6.11-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:619ee3fe03daec8d3ce12239f0c22455676a063b2bcde361caecd788fae5b8d5", size = 471488, upload-time = "2026-06-30T14:22:53.429Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/22/29/47d84a333ee00db0cff74fde2aa39e01953abb360d14ffed70ad9195af26/python_bidi-0.6.11-cp311-cp311-musllinux_1_2_armv7l.whl", hash = "sha256:af7711cc6eeeadcb1aae877e0736a68e111c73a29562da6106c5e2fbb4dd83b2", size = 576451, upload-time = "2026-06-30T14:23:05.961Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/50/b1/1c158a64d745e4916fbc39c6ad700c5b2cf3b4456f710a62f625fba0694f/python_bidi-0.6.11-cp311-cp311-musllinux_1_2_i686.whl", hash = "sha256:b2c759e13ebb81edaac3041697328bb1ca8433b55281723879f6b54e74881240", size = 537476, upload-time = "2026-06-30T14:23:18.118Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a9/7c/eeaad2247b29f736cda2632d14c5959940a4b4e8e098559cd2a4065356eb/python_bidi-0.6.11-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:b433840a924c8788f0abbda15d22c71dc636e2078d7d3ba39369cebe4bef74b8", size = 503885, upload-time = "2026-06-30T14:23:31.134Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ad/d0/ec71ea3e29cc745580ac5477bbf3c9235782ab0c7bc08ac065e4c7cb12ec/python_bidi-0.6.11-cp311-cp311-win32.whl", hash = "sha256:8b6b7fce8f47578be9aebf5a0b6b2d6c157b4e97af7586ecf13bfca5d128deea", size = 158704, upload-time = "2026-06-30T14:23:53.754Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d7/98/0458bf0adcf09f1766e069f6d1265d83b8354146e689927f0d14451bf4e2/python_bidi-0.6.11-cp311-cp311-win_amd64.whl", hash = "sha256:555cdf9303c40bae1ab512ca427f1f0316a574bc0a48db22eec76ec0fd1213cf", size = 163236, upload-time = "2026-06-30T14:23:44.741Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bd/ad/e2ff0e5077de577211d7d4fd6a436a97d903d6c65e8deb4c958de901b0eb/python_bidi-0.6.11-cp312-cp312-macosx_10_12_x86_64.whl", hash = "sha256:83ee87feb5eafc0442e1db0014dad20d52a2a7a140b6cddc8f7bc65918f0a7b4", size = 267279, upload-time = "2026-06-30T14:22:43.34Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/08/19/776b39e47e0bde27000fc2e68c2dd0bad023d4c470dcd5ec9c98779a62c8/python_bidi-0.6.11-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:2d6970b09f5a3102c0aa192f5258c585742ab4ebd94f637a635ad3448ccba567", size = 265032, upload-time = "2026-06-30T14:22:32.901Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cb/fc/87f3b820bbee3620bdd89047ee617db49700719a632d149e1c8a8c6ec59b/python_bidi-0.6.11-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:495a76c881d78ab87b57c5270679c9bc3c1de36d8c6596d5e3a5a1b5f9c57471", size = 292228, upload-time = "2026-06-30T14:21:30.411Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/d2/2b/e48c592fcd01409bd09eb1a181c31702f7c048d06bca004b8840d101f69a/python_bidi-0.6.11-cp312-cp312-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:9af2b5c26a3eb960699dff040535a86dc2c0f708087b2d63bcfd6452fe9d0664", size = 297708, upload-time = "2026-06-30T14:21:41.536Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/77/68/9da530ac64b961f5dcdcaad03d90b35becb43dd7d244241809da11acca34/python_bidi-0.6.11-cp312-cp312-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:f99162d6c6c9522c46eb213f1bc932829c2602131676c92f081cb865b8ef6784", size = 415289, upload-time = "2026-06-30T14:21:51.919Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9d/8e/ce62bfec64769c28d537ef0481a36dd9ee42795b2975b41846f1aa82f7cc/python_bidi-0.6.11-cp312-cp312-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:caee7ee3662eab1411b44fef8571b87273cb5235061d7463ebd10e412ac07986", size = 318361, upload-time = "2026-06-30T14:22:02.282Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/09/fb/57a496606a4faf051a3e851cc05a5bab2a1cd37b14fc738a707a5a51bba8/python_bidi-0.6.11-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:3611d13b53d4c899c4f2a7cd8eb897064e8b5546c3c5d1037dd6209c82858a27", size = 296198, upload-time = "2026-06-30T14:22:22.914Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7e/e9/31fbe166932d34860271ec5e0e0ecf0bc4166bddb889a260962a448d8617/python_bidi-0.6.11-cp312-cp312-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:6ca92a4e460f7e25e434a6d7982d94a4765ca242f527995da70abb5a32003b8a", size = 313188, upload-time = "2026-06-30T14:22:12.589Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c5/ea/0c2ae4a316ea919698ede4201da1c07dc1c2f9a86bd3bce3f9b94b0ce7e6/python_bidi-0.6.11-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:539d99efe02f4981171ed57bbc085094ef780405ca14663550a56c6c3e265c34", size = 468437, upload-time = "2026-06-30T14:22:54.946Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/17/6c/4795fb7f3ddca33a981158437ff6bd4c532d1011b9d887f47cff45d035e8/python_bidi-0.6.11-cp312-cp312-musllinux_1_2_armv7l.whl", hash = "sha256:9435acc52438c3c8f5142b9a17a622927618e80a32eed707343bc375cb51cebe", size = 573566, upload-time = "2026-06-30T14:23:07.133Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6e/79/8e707fb95cec1afed3fbffbf5eee93f2a73cad6f1445e17e32c6256cf397/python_bidi-0.6.11-cp312-cp312-musllinux_1_2_i686.whl", hash = "sha256:239a00b2adb5f897d11d7b7a491d759fb9883e71a71cfd90c4147a733b4df4d3", size = 533591, upload-time = "2026-06-30T14:23:19.389Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/fb/4c/5e8d01ed3d2f8ca1116e1e21085a5ca97450b4035138988e5a82ea2db916/python_bidi-0.6.11-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:51915502898c45e9cb36636e974aca068fc5cdb9f06b794f49abea5b12f02016", size = 500360, upload-time = "2026-06-30T14:23:32.445Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b0/77/86bf9c4a95f363451e7c322ef76afbe56034da23a2b5532bd774751022ff/python_bidi-0.6.11-cp312-cp312-win32.whl", hash = "sha256:6c92d1cad16f9ec2f2a3ae439a0bc3a8e4189ec227987bed03d5b4056d5eb9c5", size = 157049, upload-time = "2026-06-30T14:23:54.895Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/04/e3/8912d05e04575a60a0481cc222805331b74154940528a6f419fc5bbba744/python_bidi-0.6.11-cp312-cp312-win_amd64.whl", hash = "sha256:0608bddcc1c53dfa5293499de13ca9935b31aa46d1c722c404a88c703d1a4e47", size = 161243, upload-time = "2026-06-30T14:23:45.819Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/29/27/b4878ebed0c75833629aefe0b9cbff588df86efe6068d4c100ee8d0df27b/python_bidi-0.6.11-pp311-pypy311_pp73-macosx_10_12_x86_64.whl", hash = "sha256:8eb09af209cd660fa9689f6ce9e61e73c8afa4829ae61801deea7f6e32263800", size = 271411, upload-time = "2026-06-30T14:22:50.789Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f9/d5/080a6acda54809d12736eaf58cd71b930eba431eab78b2920727204f8a94/python_bidi-0.6.11-pp311-pypy311_pp73-macosx_11_0_arm64.whl", hash = "sha256:f7a8429d0d65232e314b4f494825c1205abcc3039f42bf7da80424a15b731709", size = 271915, upload-time = "2026-06-30T14:22:39.907Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2b/33/a076031a95627bef4e051d94ac90ff44dac687e988da4417a387e226555d/python_bidi-0.6.11-pp311-pypy311_pp73-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:c6b3c853f99172ef22e5a16c8114cf243e351c8e70f72a894164088c2c99d9cb", size = 296494, upload-time = "2026-06-30T14:21:38.138Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3d/dd/dcb312034d421f99b00d2d2b37b1a4b6ce919a3f0c70ed3aa5570b4d1bef/python_bidi-0.6.11-pp311-pypy311_pp73-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:69d1f4ee17644e8aeac93a7238ee2f28d79b0180815441eb511b77e6585aa971", size = 302105, upload-time = "2026-06-30T14:21:48.328Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/8a/e2/25192b48e4bedf7491daa7aa50e196d06b46ffa495b039d492845173bffb/python_bidi-0.6.11-pp311-pypy311_pp73-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:80cfe97e2d65981be877ae5bd2338c6919a7cc1171fc308c8b8c7c306ba6ffd8", size = 419597, upload-time = "2026-06-30T14:21:58.598Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/45/c0/1fb868cf41aab7e22cf01e8a4717c8447a43eadda8ccf367f3660c9005d8/python_bidi-0.6.11-pp311-pypy311_pp73-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:83c780f7e4c3dd3f020db75dc425567981e65c6d5571b3c0372203d0df92c834", size = 322465, upload-time = "2026-06-30T14:22:08.965Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/27/41/a7903ff2829d16eae2b11dff1cffeb9739373b8fe12c27ab04dda7c5c45d/python_bidi-0.6.11-pp311-pypy311_pp73-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4ac819cac1abb15486c48af3399a5c726e89f0977a3aff205ac162533186e756", size = 300467, upload-time = "2026-06-30T14:22:29.729Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/29/eb/16f4fb6acaf5d381da8faf26a306a255e54417450674d4c004a0ba0545ae/python_bidi-0.6.11-pp311-pypy311_pp73-manylinux_2_5_i686.manylinux1_i686.whl", hash = "sha256:63d9dcc714d549a5118ebc93b7a7c903a0c1feec8e57f5fcacf98d984b76325b", size = 318422, upload-time = "2026-06-30T14:22:19.416Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/86/04/ef0ba9e878bb5169e32bbd96e3e39ab60e947ae1757b7dd645eb4fc2e7a4/python_bidi-0.6.11-pp311-pypy311_pp73-musllinux_1_2_aarch64.whl", hash = "sha256:d73a821873c52635321196cfb8d3a231d7917ca284cf2bcd9422f6deb19db7ca", size = 473278, upload-time = "2026-06-30T14:23:03.142Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/be/93/b3aa2631c4801ddd5c22f93e430ed1d570bcb634c4a217e864195cbe7574/python_bidi-0.6.11-pp311-pypy311_pp73-musllinux_1_2_armv7l.whl", hash = "sha256:bde89739a979d9eb3ac48c4882c8a42dd528a7708b0faa99b90b551588bd5f8d", size = 577704, upload-time = "2026-06-30T14:23:15.336Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/87/b0/f5b755e1e4807bb403ccad09b1ca4faca52e260f384595e9362a68e378a6/python_bidi-0.6.11-pp311-pypy311_pp73-musllinux_1_2_i686.whl", hash = "sha256:7738cfc7ee9fcdff3fef76b40007982ce7704010a51df307c4a51d378f5ff1ba", size = 538805, upload-time = "2026-06-30T14:23:28.158Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c7/f0/f6b9d17e3426e7b54c18d05d917982647a976233ed9348a2ef40f1a17f85/python_bidi-0.6.11-pp311-pypy311_pp73-musllinux_1_2_x86_64.whl", hash = "sha256:cf4e88a6fec81b7155a487cbbea7753a3d9a76dc4d391b4f8958b37227ef2c12", size = 505204, upload-time = "2026-06-30T14:23:41.222Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "python-dateutil"
|
name = "python-dateutil"
|
||||||
version = "2.9.0.post0"
|
version = "2.9.0.post0"
|
||||||
@@ -1551,6 +1916,19 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/86/06/be4f6b337d773ae5739a1bc238f97c16926e72017243735853c030f4c628/regex-2026.6.28-cp312-cp312-win_arm64.whl", hash = "sha256:37294d3d7ddb64c7e89184b2894e0f8f0a19c514bc59513d71fe692c3a8d5fc6", size = 277022, upload-time = "2026-06-28T19:54:32.97Z" },
|
{ url = "https://files.pythonhosted.org/packages/86/06/be4f6b337d773ae5739a1bc238f97c16926e72017243735853c030f4c628/regex-2026.6.28-cp312-cp312-win_arm64.whl", hash = "sha256:37294d3d7ddb64c7e89184b2894e0f8f0a19c514bc59513d71fe692c3a8d5fc6", size = 277022, upload-time = "2026-06-28T19:54:32.97Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "reportlab"
|
||||||
|
version = "4.5.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "charset-normalizer" },
|
||||||
|
{ name = "pillow" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/4d/3f/b3861b7e40c9d66f4a04e018958d681d16b948bfd1963c962d43a8c23f66/reportlab-4.5.1.tar.gz", hash = "sha256:9fdf68f4de9171ec66acb4a5feed8f8ca2af43479e707a6fbb0daa75d88e5494", size = 3939748, upload-time = "2026-05-12T10:14:13.663Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a7/45/ea7fad10122440de6e845568d106bffdc456ca0e8a1d8ae10b46016087e4/reportlab-4.5.1-py3-none-any.whl", hash = "sha256:06fce8cb56c83307cfa4909cdf4e6a2ddbb44e5d6ef4d2edca896d7e9769f091", size = 1957812, upload-time = "2026-05-12T10:14:10.622Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "requests"
|
name = "requests"
|
||||||
version = "2.34.2"
|
version = "2.34.2"
|
||||||
@@ -1666,6 +2044,68 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/d7/2b/9555445e1201d92b3195f45cdb153a0b68f24e0a4273f6e3d5ab46e212bb/ruff-0.15.20-py3-none-win_arm64.whl", hash = "sha256:2f5b2a6d614e8700388806a14996c40fab2c47b819ef57d790a34878858ed9ca", size = 11343498, upload-time = "2026-06-25T17:20:35.03Z" },
|
{ url = "https://files.pythonhosted.org/packages/d7/2b/9555445e1201d92b3195f45cdb153a0b68f24e0a4273f6e3d5ab46e212bb/ruff-0.15.20-py3-none-win_arm64.whl", hash = "sha256:2f5b2a6d614e8700388806a14996c40fab2c47b819ef57d790a34878858ed9ca", size = 11343498, upload-time = "2026-06-25T17:20:35.03Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scipy"
|
||||||
|
version = "1.17.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
resolution-markers = [
|
||||||
|
"python_full_version < '3.12' and sys_platform == 'win32'",
|
||||||
|
"python_full_version < '3.12' and sys_platform == 'emscripten'",
|
||||||
|
"python_full_version < '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'",
|
||||||
|
]
|
||||||
|
dependencies = [
|
||||||
|
{ name = "numpy", version = "2.4.6", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/7a/97/5a3609c4f8d58b039179648e62dd220f89864f56f7357f5d4f45c29eb2cc/scipy-1.17.1.tar.gz", hash = "sha256:95d8e012d8cb8816c226aef832200b1d45109ed4464303e997c5b13122b297c0", size = 30573822, upload-time = "2026-02-23T00:26:24.851Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/df/75/b4ce781849931fef6fd529afa6b63711d5a733065722d0c3e2724af9e40a/scipy-1.17.1-cp311-cp311-macosx_10_14_x86_64.whl", hash = "sha256:1f95b894f13729334fb990162e911c9e5dc1ab390c58aa6cbecb389c5b5e28ec", size = 31613675, upload-time = "2026-02-23T00:16:00.13Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f7/58/bccc2861b305abdd1b8663d6130c0b3d7cc22e8d86663edbc8401bfd40d4/scipy-1.17.1-cp311-cp311-macosx_12_0_arm64.whl", hash = "sha256:e18f12c6b0bc5a592ed23d3f7b891f68fd7f8241d69b7883769eb5d5dfb52696", size = 28162057, upload-time = "2026-02-23T00:16:09.456Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6d/ee/18146b7757ed4976276b9c9819108adbc73c5aad636e5353e20746b73069/scipy-1.17.1-cp311-cp311-macosx_14_0_arm64.whl", hash = "sha256:a3472cfbca0a54177d0faa68f697d8ba4c80bbdc19908c3465556d9f7efce9ee", size = 20334032, upload-time = "2026-02-23T00:16:17.358Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/ec/e6/cef1cf3557f0c54954198554a10016b6a03b2ec9e22a4e1df734936bd99c/scipy-1.17.1-cp311-cp311-macosx_14_0_x86_64.whl", hash = "sha256:766e0dc5a616d026a3a1cffa379af959671729083882f50307e18175797b3dfd", size = 22709533, upload-time = "2026-02-23T00:16:25.791Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/4d/60/8804678875fc59362b0fb759ab3ecce1f09c10a735680318ac30da8cd76b/scipy-1.17.1-cp311-cp311-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:744b2bf3640d907b79f3fd7874efe432d1cf171ee721243e350f55234b4cec4c", size = 33062057, upload-time = "2026-02-23T00:16:36.931Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/09/7d/af933f0f6e0767995b4e2d705a0665e454d1c19402aa7e895de3951ebb04/scipy-1.17.1-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:43af8d1f3bea642559019edfe64e9b11192a8978efbd1539d7bc2aaa23d92de4", size = 35349300, upload-time = "2026-02-23T00:16:49.108Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b4/3d/7ccbbdcbb54c8fdc20d3b6930137c782a163fa626f0aef920349873421ba/scipy-1.17.1-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:cd96a1898c0a47be4520327e01f874acfd61fb48a9420f8aa9f6483412ffa444", size = 35127333, upload-time = "2026-02-23T00:17:01.293Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/e8/19/f926cb11c42b15ba08e3a71e376d816ac08614f769b4f47e06c3580c836a/scipy-1.17.1-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:4eb6c25dd62ee8d5edf68a8e1c171dd71c292fdae95d8aeb3dd7d7de4c364082", size = 37741314, upload-time = "2026-02-23T00:17:12.576Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/95/da/0d1df507cf574b3f224ccc3d45244c9a1d732c81dcb26b1e8a766ae271a8/scipy-1.17.1-cp311-cp311-win_amd64.whl", hash = "sha256:d30e57c72013c2a4fe441c2fcb8e77b14e152ad48b5464858e07e2ad9fbfceff", size = 36607512, upload-time = "2026-02-23T00:17:23.424Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/68/7f/bdd79ceaad24b671543ffe0ef61ed8e659440eb683b66f033454dcee90eb/scipy-1.17.1-cp311-cp311-win_arm64.whl", hash = "sha256:9ecb4efb1cd6e8c4afea0daa91a87fbddbce1b99d2895d151596716c0b2e859d", size = 24599248, upload-time = "2026-02-23T00:17:34.561Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/35/48/b992b488d6f299dbe3f11a20b24d3dda3d46f1a635ede1c46b5b17a7b163/scipy-1.17.1-cp312-cp312-macosx_10_14_x86_64.whl", hash = "sha256:35c3a56d2ef83efc372eaec584314bd0ef2e2f0d2adb21c55e6ad5b344c0dcb8", size = 31610954, upload-time = "2026-02-23T00:17:49.855Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b2/02/cf107b01494c19dc100f1d0b7ac3cc08666e96ba2d64db7626066cee895e/scipy-1.17.1-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:fcb310ddb270a06114bb64bbe53c94926b943f5b7f0842194d585c65eb4edd76", size = 28172662, upload-time = "2026-02-23T00:18:01.64Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/cf/a9/599c28631bad314d219cf9ffd40e985b24d603fc8a2f4ccc5ae8419a535b/scipy-1.17.1-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:cc90d2e9c7e5c7f1a482c9875007c095c3194b1cfedca3c2f3291cdc2bc7c086", size = 20344366, upload-time = "2026-02-23T00:18:12.015Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/35/f5/906eda513271c8deb5af284e5ef0206d17a96239af79f9fa0aebfe0e36b4/scipy-1.17.1-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:c80be5ede8f3f8eded4eff73cc99a25c388ce98e555b17d31da05287015ffa5b", size = 22704017, upload-time = "2026-02-23T00:18:21.502Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/da/34/16f10e3042d2f1d6b66e0428308ab52224b6a23049cb2f5c1756f713815f/scipy-1.17.1-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e19ebea31758fac5893a2ac360fedd00116cbb7628e650842a6691ba7ca28a21", size = 32927842, upload-time = "2026-02-23T00:18:35.367Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/01/8e/1e35281b8ab6d5d72ebe9911edcdffa3f36b04ed9d51dec6dd140396e220/scipy-1.17.1-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:02ae3b274fde71c5e92ac4d54bc06c42d80e399fec704383dcd99b301df37458", size = 35235890, upload-time = "2026-02-23T00:18:49.188Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/c5/5c/9d7f4c88bea6e0d5a4f1bc0506a53a00e9fcb198de372bfe4d3652cef482/scipy-1.17.1-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:8a604bae87c6195d8b1045eddece0514d041604b14f2727bbc2b3020172045eb", size = 35003557, upload-time = "2026-02-23T00:18:54.74Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/65/94/7698add8f276dbab7a9de9fb6b0e02fc13ee61d51c7c3f85ac28b65e1239/scipy-1.17.1-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:f590cd684941912d10becc07325a3eeb77886fe981415660d9265c4c418d0bea", size = 37625856, upload-time = "2026-02-23T00:19:00.307Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/a2/84/dc08d77fbf3d87d3ee27f6a0c6dcce1de5829a64f2eae85a0ecc1f0daa73/scipy-1.17.1-cp312-cp312-win_amd64.whl", hash = "sha256:41b71f4a3a4cab9d366cd9065b288efc4d4f3c0b37a91a8e0947fb5bd7f31d87", size = 36549682, upload-time = "2026-02-23T00:19:07.67Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/bc/98/fe9ae9ffb3b54b62559f52dedaebe204b408db8109a8c66fdd04869e6424/scipy-1.17.1-cp312-cp312-win_arm64.whl", hash = "sha256:f4115102802df98b2b0db3cce5cb9b92572633a1197c77b7553e5203f284a5b3", size = 24547340, upload-time = "2026-02-23T00:19:12.024Z" },
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "scipy"
|
||||||
|
version = "1.18.0"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
resolution-markers = [
|
||||||
|
"python_full_version >= '3.12' and sys_platform == 'win32'",
|
||||||
|
"python_full_version >= '3.12' and sys_platform == 'emscripten'",
|
||||||
|
"python_full_version >= '3.12' and sys_platform != 'emscripten' and sys_platform != 'win32'",
|
||||||
|
]
|
||||||
|
dependencies = [
|
||||||
|
{ name = "numpy", version = "2.5.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/a7/25/c2700dfaf6442b4effaa91af24ebce5dc9d31bb4a69706313aae70d72cd0/scipy-1.18.0.tar.gz", hash = "sha256:67b2ad2ad54c72ca6d04975a9b2df8c3638c34ddd5b28738e94fc2b57929d378", size = 30774447, upload-time = "2026-06-19T15:01:43.456Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/6a/19/ca10ead60b0acc80b2b833c2c4a4f2ff753d0f58b811f70d911c7e94a25c/scipy-1.18.0-cp312-cp312-macosx_10_15_x86_64.whl", hash = "sha256:7bd21faaf5a1a3b2eff922d02db5f191b99a6518db9078a8fb23169f6d22259a", size = 31056519, upload-time = "2026-06-19T14:59:45.203Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/96/72/1e6442a00cd2924d361aa1b642ab6373ec35c6fabf311a760be9f76e0f13/scipy-1.18.0-cp312-cp312-macosx_12_0_arm64.whl", hash = "sha256:265915e79107de9f946b855e50d7470d5893ec3f54b342e1aa6201cbdcd8bb6b", size = 28681889, upload-time = "2026-06-19T14:59:48.103Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9b/2d/11dd93d21e147a73ba22bd75c0b9208d3a2e0ec76d53170ce7d9029b1015/scipy-1.18.0-cp312-cp312-macosx_14_0_arm64.whl", hash = "sha256:9ab7b758be6940954a713ee466e2043e9f6e2ed965c1fce5c91039f4be3d90a9", size = 20423580, upload-time = "2026-06-19T14:59:50.665Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/9c/01/93552f75e0d2a7dd115a45e59209c51e8d514daff02fc887d2623be06fe1/scipy-1.18.0-cp312-cp312-macosx_14_0_x86_64.whl", hash = "sha256:97b6cddaaee0a779ef6b5ca83c9604b27cc16b2b8fc22c142652df8793319fb8", size = 23054441, upload-time = "2026-06-19T14:59:53.564Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/3c/23/21f5e703643d66f21faa6b4c73195bfcad70c55efcb4f1ab327cd7c4101a/scipy-1.18.0-cp312-cp312-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:52a96e21517c7292375c0e27dd796a811f03fcea5fd4d108fdfea8145dcf17ab", size = 33968720, upload-time = "2026-06-19T14:59:56.415Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/dd/aa/1b939f6c67ed68635bb538e6752d3dacc02f66535182e939a89581a44e9c/scipy-1.18.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1f55797419e16e7f30cf88ffb3113ce0467f00cfe3f70d5c281730b21769bfc2", size = 35287115, upload-time = "2026-06-19T14:59:59.411Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/b6/ff/eec46be7e9234208f801062b53e1983085eddebd693f6c9bfb03b459830d/scipy-1.18.0-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:ad033410e2e0672ffdc1042110cef20e1c46f8fd0616cee1d44d8d58fad8fc11", size = 35577989, upload-time = "2026-06-19T15:00:02.235Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/84/ca/210d4759c7210bb7d269437421959b39a33434e2776b60c5cb8a763bb30a/scipy-1.18.0-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:4a55985d54c769c872e64b7f4c8a81cc30ef700cc04296abbbf3705439c126de", size = 37421717, upload-time = "2026-06-19T15:00:05.102Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/2b/54/9a9edb45345bd6744da5ddfb6628e5d5185920494c6a67ec45b6381004cb/scipy-1.18.0-cp312-cp312-win_amd64.whl", hash = "sha256:71ccc8faa2dd16ac310233203474a8b5cb67f10dedd54a3116d34943f4b19132", size = 36597428, upload-time = "2026-06-19T15:00:08.112Z" },
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/99/0e/33f32a2a58987e26aec0f7df252cbbad1e90ae77bdbc76f40dd4ed0cf0ea/scipy-1.18.0-cp312-cp312-win_arm64.whl", hash = "sha256:d88363fd9d8fbd3511bd273f1a49efb2a540773ddf92a91d57498ce7dd7f3e76", size = 24351481, upload-time = "2026-06-19T15:00:11.103Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "six"
|
name = "six"
|
||||||
version = "1.17.0"
|
version = "1.17.0"
|
||||||
@@ -1780,6 +2220,22 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/df/84/14c36a92fb24f8e1cea452f53b0744b5da69d52cdd2fe22e71e6fbf765d5/streamlit-1.58.0-py3-none-any.whl", hash = "sha256:4ca8a7afc5bd16a5f176ccf4be1e34e8121cad0240becd127fb58a103ea3178d", size = 9219185, upload-time = "2026-05-28T18:02:41.993Z" },
|
{ url = "https://files.pythonhosted.org/packages/df/84/14c36a92fb24f8e1cea452f53b0744b5da69d52cdd2fe22e71e6fbf765d5/streamlit-1.58.0-py3-none-any.whl", hash = "sha256:4ca8a7afc5bd16a5f176ccf4be1e34e8121cad0240becd127fb58a103ea3178d", size = 9219185, upload-time = "2026-05-28T18:02:41.993Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "svglib"
|
||||||
|
version = "2.0.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "cssselect2" },
|
||||||
|
{ name = "lxml" },
|
||||||
|
{ name = "pillow" },
|
||||||
|
{ name = "reportlab" },
|
||||||
|
{ name = "tinycss2" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/4b/1f/e9dc8f16a834b6b0d5d7c0c20faeca614bcb31cbe67c54f61e3258978289/svglib-2.0.2.tar.gz", hash = "sha256:455e473dbe7066eea55c39de8194a6a3b646b6c37a255700257fd32115ddb218", size = 1341040, upload-time = "2026-06-18T07:12:50.84Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/de/de/eb6e07fb9fedba56921f8aa1dde4f5763b7abef1f962e6fea56a99092118/svglib-2.0.2-py3-none-any.whl", hash = "sha256:0fd04f1eb593802060427d6a59ab5127d65db6b8f5750d572f7df25097e36503", size = 45730, upload-time = "2026-06-18T07:12:49.221Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tenacity"
|
name = "tenacity"
|
||||||
version = "9.1.4"
|
version = "9.1.4"
|
||||||
@@ -1815,6 +2271,18 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/aa/90/28d7f154888610aa9237e541986beb62b479df29d193a5a0617dbb1514d0/tiktoken-0.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:8f2d16e7a7c783ad81f36e457d046d1f1c8af70b22aec8a13238efe531977c41", size = 874748, upload-time = "2026-05-15T04:50:39.587Z" },
|
{ url = "https://files.pythonhosted.org/packages/aa/90/28d7f154888610aa9237e541986beb62b479df29d193a5a0617dbb1514d0/tiktoken-0.13.0-cp312-cp312-win_amd64.whl", hash = "sha256:8f2d16e7a7c783ad81f36e457d046d1f1c8af70b22aec8a13238efe531977c41", size = 874748, upload-time = "2026-05-15T04:50:39.587Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "tinycss2"
|
||||||
|
version = "1.5.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "webencodings" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/a3/ae/2ca4913e5c0f09781d75482874c3a95db9105462a92ddd303c7d285d3df2/tinycss2-1.5.1.tar.gz", hash = "sha256:d339d2b616ba90ccce58da8495a78f46e55d4d25f9fd71dfd526f07e7d53f957", size = 88195, upload-time = "2025-11-23T10:29:10.082Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/60/45/c7b5c3168458db837e8ceab06dc77824e18202679d0463f0e8f002143a97/tinycss2-1.5.1-py3-none-any.whl", hash = "sha256:3415ba0f5839c062696996998176c4a3751d18b7edaaeeb658c9ce21ec150661", size = 28404, upload-time = "2025-11-23T10:29:08.676Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "tld"
|
name = "tld"
|
||||||
version = "0.13.2"
|
version = "0.13.2"
|
||||||
@@ -1859,13 +2327,18 @@ dependencies = [
|
|||||||
{ name = "langgraph" },
|
{ name = "langgraph" },
|
||||||
{ name = "langgraph-checkpoint-sqlite" },
|
{ name = "langgraph-checkpoint-sqlite" },
|
||||||
{ name = "loguru" },
|
{ name = "loguru" },
|
||||||
|
{ name = "markdown" },
|
||||||
|
{ name = "matplotlib" },
|
||||||
{ name = "pandas" },
|
{ name = "pandas" },
|
||||||
{ name = "pydantic" },
|
{ name = "pydantic" },
|
||||||
{ name = "pydantic-settings" },
|
{ name = "pydantic-settings" },
|
||||||
{ name = "python-dotenv" },
|
{ name = "python-dotenv" },
|
||||||
|
{ name = "scipy", version = "1.17.1", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version < '3.12'" },
|
||||||
|
{ name = "scipy", version = "1.18.0", source = { registry = "https://pypi.org/simple" }, marker = "python_full_version >= '3.12'" },
|
||||||
{ name = "streamlit" },
|
{ name = "streamlit" },
|
||||||
{ name = "tenacity" },
|
{ name = "tenacity" },
|
||||||
{ name = "trafilatura" },
|
{ name = "trafilatura" },
|
||||||
|
{ name = "xhtml2pdf" },
|
||||||
{ name = "yfinance" },
|
{ name = "yfinance" },
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1886,13 +2359,17 @@ requires-dist = [
|
|||||||
{ name = "langgraph" },
|
{ name = "langgraph" },
|
||||||
{ name = "langgraph-checkpoint-sqlite" },
|
{ name = "langgraph-checkpoint-sqlite" },
|
||||||
{ name = "loguru" },
|
{ name = "loguru" },
|
||||||
|
{ name = "markdown", specifier = ">=3.10.2" },
|
||||||
|
{ name = "matplotlib", specifier = ">=3.11.0" },
|
||||||
{ name = "pandas" },
|
{ name = "pandas" },
|
||||||
{ name = "pydantic", specifier = ">=2.7" },
|
{ name = "pydantic", specifier = ">=2.7" },
|
||||||
{ name = "pydantic-settings", specifier = ">=2.3" },
|
{ name = "pydantic-settings", specifier = ">=2.3" },
|
||||||
{ name = "python-dotenv" },
|
{ name = "python-dotenv" },
|
||||||
|
{ name = "scipy", specifier = ">=1.17.1" },
|
||||||
{ name = "streamlit" },
|
{ name = "streamlit" },
|
||||||
{ name = "tenacity" },
|
{ name = "tenacity" },
|
||||||
{ name = "trafilatura" },
|
{ name = "trafilatura" },
|
||||||
|
{ name = "xhtml2pdf", specifier = ">=0.2.17" },
|
||||||
{ name = "yfinance" },
|
{ name = "yfinance" },
|
||||||
]
|
]
|
||||||
|
|
||||||
@@ -1962,6 +2439,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/9e/a4/017a7a6cbe387d961a688ec31364ae60a5c4e22c96ae9921b79a947c855d/tzlocal-5.4.4-py3-none-any.whl", hash = "sha256:aae09f0126a8a86fa736be266eb4a471380d26a0de3bc14844e7821fee3e2a15", size = 18115, upload-time = "2026-06-29T08:03:38.666Z" },
|
{ url = "https://files.pythonhosted.org/packages/9e/a4/017a7a6cbe387d961a688ec31364ae60a5c4e22c96ae9921b79a947c855d/tzlocal-5.4.4-py3-none-any.whl", hash = "sha256:aae09f0126a8a86fa736be266eb4a471380d26a0de3bc14844e7821fee3e2a15", size = 18115, upload-time = "2026-06-29T08:03:38.666Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "uritools"
|
||||||
|
version = "6.1.2"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/d7/75/b1f0e26e0c080c2febdd3a98a752c9fbcf078778f60fe90ea489dc8226ed/uritools-6.1.2.tar.gz", hash = "sha256:fa60028843a8be651699a1ee2b399066eeaef349224b32a177efa4aeba463f00", size = 24189, upload-time = "2026-05-21T23:11:11.258Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/7b/3f/44a8c5de526ad21bd540015fd44987da69469ea9f51b7c9ac6a8b475a4a8/uritools-6.1.2-py3-none-any.whl", hash = "sha256:5682f8c58e96e379224fd72aec227a45432740c1fa860a06b3024d47c2968601", size = 11991, upload-time = "2026-05-21T23:11:10.002Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "urllib3"
|
name = "urllib3"
|
||||||
version = "2.7.0"
|
version = "2.7.0"
|
||||||
@@ -2046,6 +2532,15 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" },
|
{ url = "https://files.pythonhosted.org/packages/33/e8/e40370e6d74ddba47f002a32919d91310d6074130fe4e17dabcafc15cbf1/watchdog-6.0.0-py3-none-win_ia64.whl", hash = "sha256:a1914259fa9e1454315171103c6a30961236f508b9b623eae470268bbcc6a22f", size = 79067, upload-time = "2024-11-01T14:07:11.845Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "webencodings"
|
||||||
|
version = "0.5.1"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/0b/02/ae6ceac1baeda530866a85075641cec12989bd8d31af6d5ab4a3e8c92f47/webencodings-0.5.1.tar.gz", hash = "sha256:b36a1c245f2d304965eb4e0a82848379241dc04b865afcc4aab16748587e1923", size = 9721, upload-time = "2017-04-05T20:21:34.189Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/f4/24/2a3e3df732393fed8b3ebf2ec078f05546de641fe1b667ee316ec1dcf3b7/webencodings-0.5.1-py2.py3-none-any.whl", hash = "sha256:a0af1213f3c2226497a97e2b3aa01a7e4bee4f403f95be16fc9acd2947514a78", size = 11774, upload-time = "2017-04-05T20:21:32.581Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "websockets"
|
name = "websockets"
|
||||||
version = "15.0.1"
|
version = "15.0.1"
|
||||||
@@ -2086,6 +2581,26 @@ wheels = [
|
|||||||
{ url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" },
|
{ url = "https://files.pythonhosted.org/packages/e1/07/c6fe3ad3e685340704d314d765b7912993bcb8dc198f0e7a89382d37974b/win32_setctime-1.2.0-py3-none-any.whl", hash = "sha256:95d644c4e708aba81dc3704a116d8cbc974d70b3bdb8be1d150e36be6e9d1390", size = 4083, upload-time = "2024-12-07T15:28:26.465Z" },
|
||||||
]
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "xhtml2pdf"
|
||||||
|
version = "0.2.17"
|
||||||
|
source = { registry = "https://pypi.org/simple" }
|
||||||
|
dependencies = [
|
||||||
|
{ name = "arabic-reshaper" },
|
||||||
|
{ name = "html5lib" },
|
||||||
|
{ name = "pillow" },
|
||||||
|
{ name = "pyhanko" },
|
||||||
|
{ name = "pyhanko-certvalidator" },
|
||||||
|
{ name = "pypdf" },
|
||||||
|
{ name = "python-bidi" },
|
||||||
|
{ name = "reportlab" },
|
||||||
|
{ name = "svglib" },
|
||||||
|
]
|
||||||
|
sdist = { url = "https://files.pythonhosted.org/packages/da/9a/3b29831d8617ecbcf0b0aaa2b3e1b24f3fd1bbd204678ae86e9fee2f4239/xhtml2pdf-0.2.17.tar.gz", hash = "sha256:09ddbc31aa0e38a16f2f3cb73be89af5f7c968c17a564afdd685d280e39c526d", size = 139727, upload-time = "2025-02-23T23:17:02.484Z" }
|
||||||
|
wheels = [
|
||||||
|
{ url = "https://files.pythonhosted.org/packages/93/ca/d53764f0534ff857239595f090f4cb83b599d226cc326c7de5eb3d802715/xhtml2pdf-0.2.17-py3-none-any.whl", hash = "sha256:61a7ecac829fed518f7dbcb916e9d56bea6e521e02e54644b3d0ca33f0658315", size = 125349, upload-time = "2025-02-24T20:44:42.604Z" },
|
||||||
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "xxhash"
|
name = "xxhash"
|
||||||
version = "3.8.0"
|
version = "3.8.0"
|
||||||
|
|||||||
+28
-2
@@ -71,8 +71,8 @@ if not latest.empty:
|
|||||||
else:
|
else:
|
||||||
c1.info("No account snapshot yet — run execution once Alpaca keys are set.")
|
c1.info("No account snapshot yet — run execution once Alpaca keys are set.")
|
||||||
|
|
||||||
tab_port, tab_theses, tab_orders, tab_logs = st.tabs(
|
tab_port, tab_theses, tab_reports, tab_orders, tab_logs = st.tabs(
|
||||||
["📊 Portfolio", "🧠 Tonight's Theses", "🧾 Orders", "📜 Logs"]
|
["📊 Portfolio", "🧠 Tonight's Theses", "📝 Reports", "🧾 Orders", "📜 Logs"]
|
||||||
)
|
)
|
||||||
|
|
||||||
# ---------------- portfolio ----------------
|
# ---------------- portfolio ----------------
|
||||||
@@ -132,6 +132,32 @@ with tab_theses:
|
|||||||
if out.errors:
|
if out.errors:
|
||||||
st.warning("errors: " + "; ".join(out.errors))
|
st.warning("errors: " + "; ".join(out.errors))
|
||||||
|
|
||||||
|
# ---------------- reports ----------------
|
||||||
|
with tab_reports:
|
||||||
|
rdir = S.reports_dir
|
||||||
|
rdates = (
|
||||||
|
sorted((p.name for p in rdir.iterdir() if p.is_dir()), reverse=True)
|
||||||
|
if rdir.exists() else []
|
||||||
|
)
|
||||||
|
if not rdates:
|
||||||
|
st.info("No reports yet — generated during the nightly run (state/reports/).")
|
||||||
|
else:
|
||||||
|
rdate = st.selectbox("Report date", rdates, key="rdate")
|
||||||
|
ddir = rdir / rdate
|
||||||
|
# daily.md first, then each agent
|
||||||
|
mds = sorted(ddir.glob("*.md"), key=lambda p: (p.name != "daily.md", p.name))
|
||||||
|
for md in mds:
|
||||||
|
st.markdown(md.read_text())
|
||||||
|
pdf = md.with_suffix(".pdf")
|
||||||
|
if pdf.exists():
|
||||||
|
st.download_button(
|
||||||
|
f"⬇ Download {pdf.name}", pdf.read_bytes(), file_name=pdf.name,
|
||||||
|
mime="application/pdf", key=f"dl_{md.stem}",
|
||||||
|
)
|
||||||
|
for png in sorted(ddir.glob(f"{md.stem}_*.png")):
|
||||||
|
st.image(str(png))
|
||||||
|
st.divider()
|
||||||
|
|
||||||
# ---------------- orders ----------------
|
# ---------------- orders ----------------
|
||||||
with tab_orders:
|
with tab_orders:
|
||||||
orders = _query_df(
|
orders = _query_df(
|
||||||
|
|||||||
Reference in new issue
Block a user