Spaces:
Running
Running
File size: 12,750 Bytes
a73d60b 18ec181 44892bf 18ec181 44892bf a73d60b 18ec181 4567005 18ec181 693a85b 18ec181 693a85b 18ec181 44892bf 18ec181 44892bf 18ec181 f5c9dc1 18ec181 f5c9dc1 44892bf 18ec181 44892bf 18ec181 693a85b 18ec181 693a85b 18ec181 57dc987 18ec181 57dc987 18ec181 693a85b 18ec181 44892bf 693a85b 18ec181 693a85b 44892bf 693a85b 18ec181 46465b2 5a17ca0 46465b2 5a17ca0 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 | ---
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.
|