Spaces:
Running
Running
|
Download README.md from igardner/StockAnalysis: direct link, hf CLI and curl.
- Browser
- Download file 12.8 kB
-
https://huggingface.co/spaces/igardner/StockAnalysis/resolve/main/README.md
- Command line
-
hf download hf://spaces/igardner/StockAnalysis/README.md
-
curl -L -o README.md https://huggingface.co/spaces/igardner/StockAnalysis/resolve/main/README.md
12.8 kB
| title: StockAnalysis | |
| emoji: π | |
| colorFrom: pink | |
| colorTo: gray | |
| sdk: gradio | |
| sdk_version: 6.30.0 | |
| python_version: '3.13' | |
| app_file: app.py | |
| pinned: false | |
| license: mit | |
| short_description: Structured stock analysis as JSON for agents. | |
| suggested_hardware: cpu-basic | |
| tags: | |
| - stock-analysis | |
| - finance | |
| - agents | |
| - gradio | |
| - huggingface-hub | |
| - inference-providers | |
| - smoke-test | |
| - model-evaluation | |
| models: | |
| - Qwen/Qwen3.8-27B | |
| - Qwen/Qwen3.8-Flash-Next | |
| - meta-models/Muse-Spark-Open-816B-A42B | |
| - reflection/Beam-1.0-501B-A23B | |
| - deepseek-ai/DeepSeek-V4.1-Flash | |
| - meta-llama/Meta-Llama-3-8B-Instruct | |
| - nvidia/NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-NVFP4 | |
| - moonshotai/Kimi-K3 | |
| - zai-org/GLM-5.3-Flash | |
| - meta-llama/Llama-3.1-8B-Instruct | |
| - meta-models/Muse-Spark-Open-816B-A42B-NVFP4 | |
| - OBLITERATUS/Qwen3.8-27B-OBLITERATED | |
| - zai-org/GLM-5.3 | |
| - meta-models/Muse-Glimmer-30B | |
| - google/gemma-4-31B-it | |
| - Qwen/Qwen3.5-9B | |
| - meta-llama/Llama-3.2-3B-Instruct | |
| - XiaomiMiMo/MiMo-V2.6-Pro-RL | |
| - inclusionAI/Ling-3.1-flash | |
| - Qwen/Qwen3.5-4B | |
| - openai/gpt-oss-20b | |
| - google/gemma-4-26B-A4B-it | |
| - Qwen/Qwen3.6-35B-A3B | |
| - huihui-ai/Huihui-Qwen3.8-27B-abliterated | |
| - openai/gpt-oss-120b | |
| - XiaomiMiMo/MiMo-V2.6-Flash-RL | |
| - Qwen/Qwen2.5-1.5B-Instruct | |
| - Qwen/Qwen3-0.6B | |
| - Qwen/Qwen3-8B | |
| - deepseek-ai/DeepSeek-R1 | |
| # StockAnalysis | |
| StockAnalysis returns stock-analysis output as JSON for humans and agents. | |
| The main screen collects a ticker, horizon, risk profile, and an optional | |
| question. The Space sends that request to a separate stock-analysis project and | |
| returns the upstream result as JSON, with the raw response kept beside it. If | |
| the upstream project returns text, an optional local model can convert it to | |
| JSON without inventing prices, targets, or recommendations. | |
| The original inference smoke test is still here. It now lives in a compact panel | |
| fixed to the bottom-right corner of the page, with the live model table, run | |
| history, JSON download, and settings. | |
| ## Agent API | |
| There is no separate REST layer. Agents call the normal Gradio event behind the | |
| **Analyze** button. The Hub also generates a live `agents.md` for the Space. The | |
| repository copy at [`agents.md`](./agents.md) adds concrete examples. | |
| ```text | |
| To use this application (igardner/StockAnalysis: Structured stock analysis as JSON for agents): | |
| API schema: GET https://igardner-stockanalysis.hf.space/gradio_api/info | |
| Call endpoint: POST https://igardner-stockanalysis.hf.space/gradio_api/call/v2/analyze {"ticker": "AAPL", "horizon": "3 months", "risk_profile": "balanced", "question": ""} | |
| Poll result: GET https://igardner-stockanalysis.hf.space/gradio_api/call/analyze/{event_id} | |
| File inputs: none - all inputs are JSON scalars. | |
| Auth: Bearer $HF_TOKEN (https://huggingface.co/settings/tokens) | |
| ``` | |
| The endpoint returns three values in order: the JSON result, the raw upstream | |
| response, and a short human-readable status string. The JSON result is the value | |
| agents should use. See [`agents.md`](./agents.md) for the full boundary and | |
| promise list, error shapes, retry guidance, and the private smoke-test caveat. | |
| ```bash | |
| EVENT_ID=$(curl -sS -X POST \ | |
| "https://igardner-stockanalysis.hf.space/gradio_api/call/v2/analyze" \ | |
| -H "Content-Type: application/json" \ | |
| -H "Authorization: Bearer $HF_TOKEN" \ | |
| -d '{"ticker":"AAPL","horizon":"3 months","risk_profile":"balanced","question":""}' \ | |
| | python -c 'import json,sys; print(json.load(sys.stdin)["event_id"])') | |
| curl -sS -N \ | |
| "https://igardner-stockanalysis.hf.space/gradio_api/call/analyze/$EVENT_ID" \ | |
| -H "Authorization: Bearer $HF_TOKEN" | |
| ``` | |
| Always fetch `/gradio_api/info` before calling. The schema is the runtime | |
| contract, and Gradio can change the generated examples between versions. | |
| ## Configuration | |
| | Variable | Required | Default | Purpose | | |
| |---|---|---|---| | |
| | `EXTERNAL_API_URL` | for real results | blank | HTTP endpoint of the external stock-analysis project. | | |
| | `STOCK_MODEL_ID` | no | blank | Uploaded custom model ID. Sent as `model` when set. | | |
| | `LOCAL_PARSER_URL` | no | blank | OpenAI-compatible chat endpoint used to convert text to JSON. | | |
| | `LOCAL_PARSER_MODEL` | no | `llama3.1` | Model name for the local parser. | | |
| | `HF_TOKEN` | for smoke test and private calls | Space secret | Token read with `os.getenv("HF_TOKEN")`. Never committed. | | |
| `EXTERNAL_API_URL` is blank until the external project is ready. Until it is | |
| set, the app returns a clear `not_configured` JSON response instead of failing | |
| silently. `STOCK_MODEL_ID` is also blank until the custom model is uploaded. | |
| Add `HF_TOKEN` under **Settings β Variables and secrets β New secret** in the | |
| Space. Secrets are injected as environment variables and are write-only in the | |
| UI. Restart the Space after adding or changing one. The app never writes the | |
| token to disk or into run results. | |
| ## Custom model | |
| The custom stock-analysis model is not wired in yet. Set `STOCK_MODEL_ID` to its | |
| Hub model ID (or to whatever identifier the external project expects) when it is | |
| ready. If it is blank, the external request omits the `model` field. | |
| ## Local JSON parser | |
| The parser is optional. It is useful when the external project returns text but | |
| an agent needs JSON. It calls any OpenAI-compatible chat-completions endpoint and | |
| asks the model to preserve the source facts. | |
| With Ollama: | |
| ```bash | |
| ollama serve | |
| ollama pull llama3.1 | |
| export LOCAL_PARSER_URL=http://127.0.0.1:11434/v1 | |
| export LOCAL_PARSER_MODEL=llama3.1 | |
| ``` | |
| The app also accepts a full `/chat/completions` URL. A base URL ending in `/v1` | |
| works, and a bare host gets `/v1/chat/completions` appended. | |
| On Hugging Face Spaces, `127.0.0.1` points at the Space container. Use a | |
| reachable parser endpoint or a Docker Space with a sidecar if you want the local | |
| parser to run next to the app. The normal deployment path is the external | |
| project plus its own JSON output. | |
| ## External project contract | |
| The adapter in `external_api.py` sends: | |
| ```json | |
| { | |
| "ticker": "AAPL", | |
| "horizon": "3 months", | |
| "risk_profile": "balanced", | |
| "question": "" | |
| } | |
| ``` | |
| It adds `"model": "<STOCK_MODEL_ID>"` when a custom model is configured. It | |
| sends `Authorization: Bearer $HF_TOKEN` when the secret is present. | |
| Response handling: | |
| - HTTP 2xx with JSON: passed through unchanged. | |
| - HTTP 2xx with text: wrapped as `{"status": "ok", "ticker": "...", "raw": "..."}` | |
| unless the local parser is configured. | |
| - Non-2xx: returned as `{"status": "error", ...}` with the HTTP status and a | |
| short error message. | |
| - No `EXTERNAL_API_URL`: returned as `{"status": "not_configured", ...}`. | |
| The adapter is the only place to change when the external request or response | |
| contract changes. | |
| ## Inference smoke test | |
| The smoke test is still the original liveness check. It queries the Hub for the | |
| most trending conversational models with a live inference provider, sends one | |
| tiny chat completion to each, and records a literal `True` or `False`. The | |
| compact bottom-right panel shows the model table, run history, download, and | |
| settings. | |
| The smoke-test events are marked `api_visibility="private"`, so they do not | |
| appear in the public API docs or the Gradio clients. Private does not block | |
| direct HTTP, so it is not a security boundary. The Analyze event is the only | |
| public endpoint. | |
| Settings live at the top of `app.py`: | |
| ```python | |
| MODEL_LIMIT = 30 # how many trending models to test | |
| CONCURRENCY = 3 # parallel requests | |
| MAX_TOKENS = 8 # output tokens per attempt | |
| REQUEST_TIMEOUT_SECONDS = 60 # per-request timeout | |
| RESULTS_DIR = "/data/hf-inference-smoke" | |
| PAGE_SIZE = 100 # Hub discovery page size | |
| MAX_SCAN = 500 # how far past MODEL_LIMIT to scan | |
| AUTO_RUN_ON_START = False # run automatically on page load | |
| ``` | |
| Every run is saved to persistent storage, not the repository: | |
| ```text | |
| /data/hf-inference-smoke/ | |
| βββ latest.json # most recent run (mirror) | |
| βββ runs/<run_id>.json # one file per run | |
| βββ models/latest.json # most recent discovery snapshot | |
| ``` | |
| Each run file records the model list, trending scores, live providers, | |
| per-attempt status, latency, usage, and errors. | |
| ### Models used | |
| The list below is the last recorded snapshot, kept so the tested models and the | |
| smoke-test history are not lost. Selection is dynamic, so the next run can use a | |
| different order. The run JSON always records the exact list it used. | |
| Snapshot: `2026-10-25T20:18:59Z` | |
| Last recorded run: `20261025T202058Z-ff6262` at `2026-10-25T20:20:58Z` - **21/30 passed** in 64.56s. | |
| | # | Model | Result | | |
| |---:|---|---| | |
| | 1 | `Qwen/Qwen3.8-27B` | True | | |
| | 2 | `Qwen/Qwen3.8-Flash-Next` | False | | |
| | 3 | `meta-models/Muse-Spark-Open-816B-A42B` | True | | |
| | 4 | `reflection/Beam-1.0-501B-A23B` | True | | |
| | 5 | `deepseek-ai/DeepSeek-V4.1-Flash` | True | | |
| | 6 | `meta-llama/Meta-Llama-3-8B-Instruct` | False | | |
| | 7 | `nvidia/NVIDIA-Nemotron-3.5-Super-VL-120B-A12B-NVFP4` | True | | |
| | 8 | `moonshotai/Kimi-K3` | True | | |
| | 9 | `zai-org/GLM-5.3-Flash` | True | | |
| | 10 | `meta-llama/Llama-3.1-8B-Instruct` | True | | |
| | 11 | `meta-models/Muse-Spark-Open-816B-A42B-NVFP4` | False | | |
| | 12 | `OBLITERATUS/Qwen3.8-27B-OBLITERATED` | False | | |
| | 13 | `zai-org/GLM-5.3` | True | | |
| | 14 | `meta-models/Muse-Glimmer-30B` | True | | |
| | 15 | `google/gemma-4-31B-it` | True | | |
| | 16 | `Qwen/Qwen3.5-9B` | True | | |
| | 17 | `meta-llama/Llama-3.2-3B-Instruct` | False | | |
| | 18 | `XiaomiMiMo/MiMo-V2.6-Pro-RL` | True | | |
| | 19 | `inclusionAI/Ling-3.1-flash` | True | | |
| | 20 | `Qwen/Qwen3.5-4B` | False | | |
| | 21 | `openai/gpt-oss-20b` | True | | |
| | 22 | `google/gemma-4-26B-A4B-it` | True | | |
| | 23 | `Qwen/Qwen3.6-35B-A3B` | True | | |
| | 24 | `huihui-ai/Huihui-Qwen3.8-27B-abliterated` | False | | |
| | 25 | `openai/gpt-oss-120b` | True | | |
| | 26 | `XiaomiMiMo/MiMo-V2.6-Flash-RL` | True | | |
| | 27 | `Qwen/Qwen2.5-1.5B-Instruct` | False | | |
| | 28 | `Qwen/Qwen3-0.6B` | False | | |
| | 29 | `Qwen/Qwen3-8B` | True | | |
| | 30 | `deepseek-ai/DeepSeek-R1` | True | | |
| ### How the smoke test works | |
| 1. Discovery queries the Hub for `conversational` models sorted by | |
| `trendingScore`, scanning past the requested count so models without a live | |
| inference provider are skipped. | |
| 2. Routing calls each model through the HF router with `:cheapest`. If that | |
| fails, it retries once with `:fastest`. | |
| 3. A model passes only when the router returns HTTP 200 with a non-empty | |
| `choices` array. | |
| 4. Each attempt asks for at most 8 output tokens, so a 30-model run costs well | |
| under a cent. | |
| 5. Every run is saved to `/data/hf-inference-smoke/`. | |
| A `False` result means the model could not produce a valid chat completion | |
| through either routing policy at the time of the run. It is a liveness check, not | |
| a quality benchmark. | |
| ## Claims and disclaimer | |
| The app is built for structured stock analysis and predictions. It does not | |
| promise a specific return, and it does not guarantee that any result will be | |
| profitable. Output comes from an external project and may be incomplete or | |
| wrong. | |
| This is research tooling, not financial advice. Markets involve risk, including | |
| loss of principal. Verify anything before acting on it. | |
| ## Project layout | |
| ```text | |
| app.py # Gradio UI, settings, public /analyze event, smoke corner | |
| external_api.py # external stock-analysis project adapter | |
| local_parser.py # optional OpenAI-compatible text-to-JSON converter | |
| smoke.py # discovery, routing, smoke runner, persistence | |
| agents.md # concrete agent contract and examples | |
| PROGRESS.md # implementation notes, committed as work proceeds | |
| requirements.txt # pinned dependencies | |
| README.md # Space metadata and documentation | |
| ``` | |
| ## Requirements | |
| - Python 3.13 and Gradio 6.30.0 via the README `sdk_version`. | |
| - `huggingface_hub==1.16.1` and `requests==2.34.2` pinned in | |
| `requirements.txt`. | |
| - CPU basic hardware is sufficient. No GPU is required. | |
| - `HF_TOKEN` should be a Space secret for the smoke test and authenticated | |
| outbound calls. | |
| ## Deployment | |
| 1. Add `HF_TOKEN` as a Space secret. | |
| 2. Set `EXTERNAL_API_URL` when the external stock-analysis project is ready. | |
| 3. Set `STOCK_MODEL_ID` when the custom model is uploaded. | |
| 4. Optionally set `LOCAL_PARSER_URL` and `LOCAL_PARSER_MODEL` for text-to-JSON | |
| conversion. | |
| 5. Restart the Space. | |
| After restart, `GET /gradio_api/info` lists the `analyze` endpoint. The smoke | |
| test remains available in the bottom-right corner and does not appear in the | |
| public endpoint list. | |
| ### Maintainer note: dev mode | |
| If the Space is in dev mode, pushed commits do not trigger a rebuild. Do not | |
| disable dev mode or run a factory reboot on a live dev container; that tears down | |
| the running environment. Use the dev-mode workflow to apply changes, or restart | |
| the Space intentionally after the dev session is finished. | |