Add live dashboard to AI Language Learning Phone Tutor#43
Add live dashboard to AI Language Learning Phone Tutor#43harpreetTelnyx wants to merge 2 commits into
Conversation
Embedded HTML dashboard at / with dark theme, SSE streaming, two-panel layout: live events (left) and conversation transcript (right). Architecture flow diagram in header. Shows caller number, language selection with flag badge, caller speech, AI tutor replies, and call duration in real time. Events emitted at every webhook stage: incoming call, answering, greeting, language selection, caller speech, AI reply, hangup. Ready for YouTube demo recording. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
Call Control webhooks were not receiving inbound calls from Telnyx. Switched to TeXML + Conversation Relay (same pattern as the Conversation Relay voice bot example). Telnyx now fetches TeXML from /texml/inbound, opens a WebSocket to /ws/conversation-relay, and the app exchanges text frames. Caller says a language name (Spanish, French, Japanese, Mandarin) to start tutoring. AI Inference (Llama-3.3-70B) runs the conversation with streaming replies. Dashboard, SSE events, and all API endpoints preserved. Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
| if base.startswith("https://"): | ||
| return "wss://" + base.removeprefix("https://") + "/ws/conversation-relay" | ||
| if base.startswith("http://"): | ||
| return "ws://" + base.removeprefix("http://") + "/ws/conversation-relay" |
There was a problem hiding this comment.
Semgrep identified an issue in your code:
User data flows into the host portion of this manually-constructed URL. This could allow an attacker to send data to their own server, potentially exposing sensitive data such as cookies or authorization information sent with this request. They could also probe internal servers or other resources that the server running this code can access. (This is called server-side request forgery, or SSRF.) Do not allow arbitrary hosts. Instead, create an allowlist for approved hosts, or hardcode the correct host.
Dataflow graph
flowchart LR
classDef invis fill:white, stroke: none
classDef default fill:#e7f5ff, color:#1c7fd6, stroke: none
subgraph File0["<b>ai-language-learning-phone-tutor-python/app.py</b>"]
direction LR
%% Source
subgraph Source
direction LR
v0["<a href=https://github.com/team-telnyx/telnyx-code-examples/blob/6cae855175443010e6db8d6146583530737020b2/ai-language-learning-phone-tutor-python/app.py#L152 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 152] request.url_root</a>"]
end
%% Intermediate
subgraph Traces0[Traces]
direction TB
v2["<a href=https://github.com/team-telnyx/telnyx-code-examples/blob/6cae855175443010e6db8d6146583530737020b2/ai-language-learning-phone-tutor-python/app.py#L156 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 156] public_base_url</a>"]
v3["<a href=https://github.com/team-telnyx/telnyx-code-examples/blob/6cae855175443010e6db8d6146583530737020b2/ai-language-learning-phone-tutor-python/app.py#L156 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 156] base</a>"]
end
v2 --> v3
%% Sink
subgraph Sink
direction LR
v1["<a href=https://github.com/team-telnyx/telnyx-code-examples/blob/6cae855175443010e6db8d6146583530737020b2/ai-language-learning-phone-tutor-python/app.py#L160 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 160] "ws://" + base.removeprefix("http://") + "/ws/conversation-relay"</a>"]
end
end
%% Class Assignment
Source:::invis
Sink:::invis
Traces0:::invis
File0:::invis
%% Connections
Source --> Traces0
Traces0 --> Sink
To resolve this comment:
🔧 No guidance has been designated for this issue. Fix according to your organization's approved methods.
💬 Ignore this finding
Reply with Semgrep commands to ignore this finding.
/fp <comment>for false positive/ar <comment>for acceptable risk/other <comment>for all other reasons
Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by tainted-url-host.
You can view more details about this finding in the Semgrep AppSec Platform.
| def conversation_relay_ws_url() -> str: | ||
| base = public_base_url() | ||
| if base.startswith("https://"): | ||
| return "wss://" + base.removeprefix("https://") + "/ws/conversation-relay" |
There was a problem hiding this comment.
Semgrep identified an issue in your code:
User data flows into the host portion of this manually-constructed URL. This could allow an attacker to send data to their own server, potentially exposing sensitive data such as cookies or authorization information sent with this request. They could also probe internal servers or other resources that the server running this code can access. (This is called server-side request forgery, or SSRF.) Do not allow arbitrary hosts. Instead, create an allowlist for approved hosts, or hardcode the correct host.
Dataflow graph
flowchart LR
classDef invis fill:white, stroke: none
classDef default fill:#e7f5ff, color:#1c7fd6, stroke: none
subgraph File0["<b>ai-language-learning-phone-tutor-python/app.py</b>"]
direction LR
%% Source
subgraph Source
direction LR
v0["<a href=https://github.com/team-telnyx/telnyx-code-examples/blob/6cae855175443010e6db8d6146583530737020b2/ai-language-learning-phone-tutor-python/app.py#L152 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 152] request.url_root</a>"]
end
%% Intermediate
subgraph Traces0[Traces]
direction TB
v2["<a href=https://github.com/team-telnyx/telnyx-code-examples/blob/6cae855175443010e6db8d6146583530737020b2/ai-language-learning-phone-tutor-python/app.py#L156 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 156] public_base_url</a>"]
v3["<a href=https://github.com/team-telnyx/telnyx-code-examples/blob/6cae855175443010e6db8d6146583530737020b2/ai-language-learning-phone-tutor-python/app.py#L156 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 156] base</a>"]
end
v2 --> v3
%% Sink
subgraph Sink
direction LR
v1["<a href=https://github.com/team-telnyx/telnyx-code-examples/blob/6cae855175443010e6db8d6146583530737020b2/ai-language-learning-phone-tutor-python/app.py#L158 target=_blank style='text-decoration:none; color:#1c7fd6'>[Line: 158] "wss://" + base.removeprefix("https://") + "/ws/conversation-relay"</a>"]
end
end
%% Class Assignment
Source:::invis
Sink:::invis
Traces0:::invis
File0:::invis
%% Connections
Source --> Traces0
Traces0 --> Sink
To resolve this comment:
🔧 No guidance has been designated for this issue. Fix according to your organization's approved methods.
💬 Ignore this finding
Reply with Semgrep commands to ignore this finding.
/fp <comment>for false positive/ar <comment>for acceptable risk/other <comment>for all other reasons
Alternatively, triage in Semgrep AppSec Platform to ignore the finding created by tainted-url-host.
You can view more details about this finding in the Semgrep AppSec Platform.
What this adds
A live dashboard at
/for the AI Language Learning Phone Tutor, designed for YouTube demo recording. Dark theme, SSE streaming, two-panel layout — same pattern as the Conversation Relay bridge dashboard.What it shows
Left panel: Live events
Every webhook event as it happens — incoming call, answering, greeting, language selection, caller speech, AI tutor reply, hangup. Each with icon, label, timestamp, and content.
Right panel: Conversation transcript
Chat-bubble view — caller said (left) vs tutor replied (right). Shows turn count, call duration, and language badge (flag + name) when a language is selected.
Header: Architecture flow
Caller dials → Telnyx Voice AI → This app (webhook) → AI Inference → reply → Telnyx TTS → Caller hears itHow it works
/events) pushes events to the browser in real time — no pollinghandle_voice()/api/statereturns current call state + conversation history for initial loadprompt,reply, andlangeventsEndpoints
GET /GET /eventsGET /api/stateGET /healthGET /sessionsPOST /webhooks/voiceRelated
Ultraworked with Sisyphus