File size: 13,121 Bytes
f755447
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
# TalkingHeadBench β€” Hackathon Compliance Change Plan

A file-by-file breakdown of every change needed to fully comply with the
[OpenEnv hackathon requirements](./hackathon_requirements.md).

---

## 1. `inference.py` (ROOT) β€” MISSING, MUST CREATE

**Status: Does not exist.** This is an immediate disqualification condition.

### What it needs to do
- Connect to the running TalkingHeadBench environment at a configurable URL.
- Run a full benchmark episode across **all 3 task tiers** (image audit, clip audit,
  weight audit) using test-set cases loaded from `tests/test_set/`.
- Use the **OpenAI Python client** (`from openai import OpenAI`) for LLM calls
  β€” not the current raw `urllib`/`httpx` approach.
- Pull LLM config exclusively from the three required environment variables:
  - `API_BASE_URL`
  - `MODEL_NAME`
  - `HF_TOKEN`
- Print a structured score report at the end: per-sub-env scores and the
  weighted final score.
- Complete in under **20 minutes** on 2 vCPU / 8 GB RAM.

### Required structure (pseudocode)
```
inference.py
β”œβ”€β”€ Load API_BASE_URL, MODEL_NAME, HF_TOKEN from os.environ
β”œβ”€β”€ Instantiate: client = OpenAI(api_key=HF_TOKEN, base_url=API_BASE_URL)
β”œβ”€β”€ For each task: "image", "clips", "weights"
β”‚   β”œβ”€β”€ Call env.reset(mode=<task>)   β€” via HTTP to the running server
β”‚   β”œβ”€β”€ Read observation, construct LLM prompt
β”‚   β”œβ”€β”€ Call client.chat.completions.create(model=MODEL_NAME, ...)
β”‚   β”œβ”€β”€ Parse structured JSON action
β”‚   β”œβ”€β”€ Call env.step(action)
β”‚   └── Record reward / scores
└── Print final score report
```

### Why `examples/simple_agent.py` does NOT qualify
- Uses raw `httpx` and manually constructed HF Inference Router calls instead of
  the OpenAI Python client.
- Reads API key from `HF_API_KEY` / `HUGGINGFACEHUB_API_TOKEN`, not from the
  required `HF_TOKEN` / `API_BASE_URL` / `MODEL_NAME` variables.
- Is not named `inference.py` and is not in the root directory.
- Does not iterate over all 3 task tiers with per-sub-env scoring output.

---

## 2. `server/llm_adapter.py` β€” LLM Client Replace

**Status: Uses raw `urllib.request` for HTTP calls β€” no OpenAI client.**

### Lines affected
- `_call_openai()` (L627–667): uses `_http_post_json()` via `urllib`. Must be
  replaced with `client.chat.completions.create(...)`.
- `_call_anthropic()` (L669–697): same issue.
- `_call_huggingface()` (L700–776): same issue β€” currently uses `urllib`
  directly to `router.huggingface.co`.
- All calls to `_http_post_json()` from provider functions should be replaced.

### Changes needed
1. Add `from openai import OpenAI` at the top.
2. Replace `_call_openai()` to use the OpenAI client:
   ```python
   client = OpenAI(api_key=api_key, base_url=base_url or "https://api.openai.com/v1")
   response = client.chat.completions.create(model=model_id, messages=..., ...)
   return response.choices[0].message.content
   ```
3. Replace `_call_huggingface()` to also use OpenAI client pointed at
   `https://router.huggingface.co/v1`:
   ```python
   client = OpenAI(api_key=api_key, base_url="https://router.huggingface.co/v1")
   ```
4. Replace `_call_anthropic()` β€” either use the Anthropic SDK or the OpenAI
   client via the Anthropic OpenAI-compatible endpoint.
5. Add `openai>=1.0` to `server/requirements.txt` and root `requirements.txt`.

### Why this matters
The hackathon rules explicitly state: **"Participants must use OpenAI Client for
all LLM calls"**. Using raw `urllib.request` violates this even if the wire
protocol is identical.

---

## 3. `server/requirements.txt` β€” ADD `openai`

**Status: Missing `openai` package.**

```diff
  openenv-core[core]>=0.2.2,<0.3
  fastapi>=0.115.0
  pydantic>=2.0
  uvicorn>=0.24.0
+ openai>=1.0
  numpy
  torch
  scipy
  safetensors
  opencv-python
  mediapipe
```

---

## 4. `requirements.txt` (ROOT) β€” ADD `openai`

**Status: Missing `openai` package.**

```diff
  numpy
  torch
  pydantic>=2.0
  scipy
  safetensors
  opencv-python
  mediapipe>=0.10.33
+ openai>=1.0
  pytest
```

---

## 5. `pyproject.toml` β€” ADD `openai` to dependencies

**Status: `openai` is absent from the package's dependency list.**

```diff
  dependencies = [
      "openenv-core[core]>=0.2.2,<0.3",
      "fastapi>=0.115.0",
      "pydantic>=2.0",
      "uvicorn>=0.24.0",
      "numpy",
      "torch",
      "scipy",
      "safetensors",
      "opencv-python",
      "mediapipe",
+     "openai>=1.0",
  ]
```

---

## 6. `server/Dockerfile` β€” VALIDATE ENV VAR PASSTHROUGH

**Status: Partially compliant. Does not explicitly declare required env vars.**

The Dockerfile currently just copies files and runs uvicorn. No issue with the
build itself, but the following should be verified / added:

```dockerfile
# Add explicit ARG/ENV declarations so evaluators can see the required
# environment variables are expected and forwarded at runtime.
ENV API_BASE_URL=""
ENV MODEL_NAME=""
ENV HF_TOKEN=""
```

Also confirm the image actually builds without network access to pip:
- `torch` is very heavy. Consider using `torch --index-url
  https://download.pytorch.org/whl/cpu` for a CPU-only build since the
  infrastructure constraint is 2 vCPU / 8 GB RAM (no GPU). This will also
  substantially reduce image size.

```diff
- RUN pip install --no-cache-dir -r /app/requirements.txt
+ RUN pip install --no-cache-dir -r /app/requirements.txt \
+     --extra-index-url https://download.pytorch.org/whl/cpu
```

---

## 7. `openenv.yaml` β€” OPTIONALLY CLEAN UP CUSTOM VARS

**Status: Mostly fine. Contains two non-standard env vars that may confuse validators.**

```yaml
# Current:
env_vars:
  - API_BASE_URL
  - MODEL_NAME
  - HF_TOKEN
  - THB_ALLOW_CUSTOM_BASE_URLS      # <-- custom, not required by spec
  - THB_ALLOWED_BASE_URL_PREFIXES   # <-- custom, not required by spec
```

These two extra env vars are not harmful, but if the validator script only
checks for the three required ones, they may be flagged as unexpected. Consider
moving them to a comment block or keeping them with a clear inline explanation.

---

## 8. `examples/simple_agent.py` β€” DOES NOT USE OPENAI CLIENT

**Status: Uses raw `httpx` + custom HF URL. Needs to be converted but is NOT
the `inference.py` required by the hackathon.**

- This file uses `httpx.Client` with a manually constructed HF router URL.
- It reads from `HF_API_KEY` / `HUGGINGFACEHUB_API_TOKEN` instead of the
  required variable names.

This file should either:
1. Be **left as-is** (fine, it's an example), but `inference.py` at root
   level must be written from scratch using the OpenAI client **and** the
   correct env var names (`API_BASE_URL`, `MODEL_NAME`, `HF_TOKEN`).
2. Or be **upgraded** to use the OpenAI client as well to avoid confusion.

---

## 9. `server/talking_head_environment.py` β€” COMBINED EPISODE REWARD FORMULA

**Status: The per-episode reward formula for individual task modes is correct.
However, the COMBINED reward formula hardcoded in `_handle_node8_action()` does
NOT match the REWARD_LOGIC.md documentation.**

### The discrepancy
- `REWARD_LOGIC.md` (and `models.py` docstring, line 12) states the final reward
  across ALL three sub-envs is:
  ```
  final_reward = 0.25 * subenv1 + 0.35 * subenv2 + 0.40 * subenv3
  ```
- But `_handle_node8_action()` (L590–591) uses:
  ```python
  final_score = 0.50 * s2 + 0.50 * s3
  ```
  …for `clips_and_weights` mode, which is a different formula and excludes subenv1.

If the hackathon evaluators run a full-episode combined audit and compare
reported scores against your REWARD_LOGIC.md, this inconsistency will be
flagged.

### Fix needed
Either:
- Update `REWARD_LOGIC.md` to explicitly document the per-mode formulas (it
  currently only documents the "global" combined formula), OR
- Implement a proper 3-way episode type that runs `subenv1 + subenv2 + subenv3`
  with the full `0.25/0.35/0.40` weighting.

---

## 10. `server/app.py` β€” `MODEL_NAME` / `API_BASE_URL` NOT USED BY SERVER ITSELF

**Status: The server reads LLM settings from the `AnalyzeIngestionRequest`
body (per-request model override), not from the required standard env vars.**

The hackathon spec requires `API_BASE_URL`, `MODEL_NAME`, and `HF_TOKEN` to be
defined in the environment configuration and used for all LLM calls. Currently:

- `API_BASE_URL` and `MODEL_NAME` are declared in `openenv.yaml` as expected vars
  but the server's `analyze_ingestion` endpoint ignores them β€” it reads
  `request.model_id` and `request.base_url` from the request body instead.
- The env vars are never fetched with `os.environ.get("MODEL_NAME")` etc.

### Fix needed
In `server/app.py` (or `server/llm_adapter.py`), the `analyze_ingested_bundle`
function should default to the env vars when the request body fields are absent:

```python
import os

model_id = request.model_id or os.environ.get("MODEL_NAME")
api_key  = request.api_key  or os.environ.get("HF_TOKEN")
base_url = request.base_url or os.environ.get("API_BASE_URL")
```

---

## 11. README.md β€” MISSING ACTION/OBSERVATION SPACE TABLE

**Status: The README documents architecture, scoring, and deployment. However,
it lacks a dedicated, explicit Action Space / Observation Space section.**

The hackathon spec requires:
> "README with environment description, action/observation spaces, setup instructions"

The current README has architecture diagrams and episode flow tables but no
clean, dedicated section labelled "Action Space" / "Observation Space" that
clearly lists all fields, types, and ranges in a format a new agent developer
could immediately use.

### Add a section like:
```markdown
## Action & Observation Spaces

### Observation Space (reset output per mode)
| Mode    | Schema                      | Key Fields                              |
|---------|-----------------------------|-----------------------------------------|
| image   | ImageDiagnosticsObservation | face_occupancy_ratio, yaw_degrees, ...  |
| clips   | ClipDispositionObservation  | evidence_dossier, marginal_drift, ...   |
| weights | PhonemeRiskObservation      | layer_entropy, rank_utilization, ...    |

### Action Space (step input per mode)
| Mode    | Schema                  | Key Fields                                   |
|---------|-------------------------|----------------------------------------------|
| image   | ImageDiagnosticsAction  | regime_classification, risk_factors, score   |
| clips   | ClipDispositionAction   | disposition, fix_instructions, override      |
| weights | PhonemeRiskAction       | phoneme_risk_ranking, mitigation_recs, ...   |

All numeric reward outputs are bounded in [0.0, 1.0].
```

---

## 12. `examples/simple_agent.py` β€” ESCAPE SEQUENCES BUG

**Status: Minor but causes broken prompts at runtime.**

Lines 282–288: The `build_user_prompt()` function uses Python literal `\\n`
(escaped backslash + n) inside a regular string, which means the actual prompt
string will contain the two characters `\n` instead of a real newline:

```python
# Current (BROKEN β€” sends literal \n characters):
f"Environment step index: {step_index}\\n"

# Should be:
f"Environment step index: {step_index}\n"
```

This affects all agent prompts and will cause the LLM to receive mangled
single-line instructions rather than well-structured multi-line prompts.

---

## 13. `server/llm_adapter.py` β€” `_call_local()` FALLS THROUGH TO OLLAMA FORMAT

**Status: When no `base_url` is provided for the `local` provider, it sends
requests to the Ollama native API format (`/api/generate`) with a `prompt` key
rather than the OpenAI-compatible chat completions format.**

This means if evaluators provide `API_BASE_URL` pointing to any OpenAI-compatible
local server (vLLM, LM Studio, etc.), the request format will be wrong.

### Fix needed
`_call_local()` should always use the OpenAI-compatible endpoint:
```python
client = OpenAI(api_key="local", base_url=base_url or "http://localhost:11434/v1")
response = client.chat.completions.create(...)
```

---

## Summary Priority Table

| Priority | File | Change |
|----------|------|--------|
| 🚨 CRITICAL | `inference.py` | CREATE from scratch using OpenAI client + correct env vars |
| 🚨 CRITICAL | `server/llm_adapter.py` | Replace all `urllib`/`httpx` calls with OpenAI Python client |
| 🚨 CRITICAL | `server/requirements.txt` | Add `openai>=1.0` |
| 🚨 CRITICAL | `requirements.txt` | Add `openai>=1.0` |
| 🚨 CRITICAL | `pyproject.toml` | Add `openai>=1.0` to dependencies |
| πŸ”΄ HIGH | `server/app.py` | Default `model_id`, `api_key`, `base_url` from env vars |
| πŸ”΄ HIGH | `README.md` | Add explicit Action/Observation Space section |
| 🟑 MEDIUM | `server/Dockerfile` | Declare ENV vars; switch to CPU torch wheel |
| 🟑 MEDIUM | `server/talking_head_environment.py` | Clarify/fix reward formula discrepancy vs REWARD_LOGIC.md |
| 🟑 MEDIUM | `examples/simple_agent.py` | Fix `\\n` escape sequence bug in prompt builder |
| 🟒 LOW | `openenv.yaml` | Annotate/clean up non-standard env vars |
| 🟒 LOW | `examples/simple_agent.py` | Switch to OpenAI client + correct env var names |