Skip to content

Add the Insights page: usage chat and takeaways on hototel's own Modal model - #50

Merged
eddietejeda merged 1 commit into
mainfrom
insights-page
Oct 17, 2026
Merged

eddietejeda merged 1 commit into
mainfrom
insights-page

Conversation

@eddietejeda

Copy link
Copy Markdown
Contributor

Replaces #49, which GitHub closed when its base branch was deleted after #47 merged. Same change, rebased onto main.

A new /insights page: members ask questions about the team's AI coding usage and see generated takeaways (good, valuable, redundant work) for the last 30 days. A LangChain agent runs SQL through hotdata-langchain tools fixed to the viewer's org database, so it reads only that org. Every answer lists its queries.

The model is hototel's own deployment on Modal (modal/hototel_llm.py, Qwen3-30B-A3B-Instruct-2507 on vLLM, one H100, scales to zero). Anthropic API stays as an alternative.

Operator setup after merge (App Runner env vars, otherwise the page shows "Insights are off"):

HOTOTEL_INSIGHTS_BASE_URL=https://hotdata--hototel-server.us-east.modal.direct/v1
HOTOTEL_INSIGHTS_API_KEY=<value of Modal secret hototel-llm>

Routes: GET /insights, /api/insights/status, /api/insights/takeaways, POST /api/insights/chat. Cookie sessions only, read tokens refused, rate limited, max four concurrent agent runs per process. Tests: server/tests/test_insights.py; all suites pass.

…, on hototel's own model

A new /insights page lets a signed-in member ask questions about how the
team uses Claude Code, Codex and OpenCode, and shows a generated set of
takeaways (good work, valuable work, redundant work) for the last 30 days.
A LangChain agent answers by running SQL through hotdata-langchain tools
scoped at build time to the viewer's org database, so it can only read
that organization's usage. Every answer lists the queries it ran.

The model is hototel's own deployment on Modal (modal/hototel_llm.py):
Qwen3-30B-A3B-Instruct-2507 behind vLLM's OpenAI-compatible API on one
H100, scaling to zero when idle, so usage rows stay inside the company's
accounts. The Anthropic API remains an alternative via ANTHROPIC_API_KEY.

Routes: GET /insights, /api/insights/status, /api/insights/takeaways and
POST /api/insights/chat, cookie sessions only (read tokens are refused),
behind the verify gate, rate limited per IP, at most four agent runs at
once per process. With no model configured the page says so and the API
answers 503.
@eddietejeda
eddietejeda requested a review from a team as a code owner October 17, 2026 02:59
@eddietejeda
eddietejeda requested review from rohan-hotdata and removed request for a team October 17, 2026 02:59
Comment thread server/server.py
self._json({"error": "you do not belong to an organization yet"}, 409)
return
body = self._read_json(limit=256 * 1024)
history = insights.validate_history(body.get("messages"))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit: Check that body is a dict before calling body.get (not blocking).

A JSON array body such as [] makes body.get raise AttributeError. The generic handler then logs the error and returns a 500, not a 400.

Suggested change
history = insights.validate_history(body.get("messages"))
if not isinstance(body, dict):
raise ValueError("send a JSON object with messages")
history = insights.validate_history(body.get("messages"))

@eddietejeda
eddietejeda merged commit b4c63f4 into main Oct 17, 2026
2 checks passed
@eddietejeda
eddietejeda deleted the insights-page branch October 17, 2026 03:02
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant