Put the capabilities into a usable screen

Build a Streamlit review interface

Connect a deliberate submit action to GenAI Studio, handle drafts and errors, and plan persistence.

Prepared September 14, 2026 · SDK source reviewed at c9e8b34 · Examples are teaching adaptations; authenticated gateway calls were not run for these pages.

Guide library

What this capability does

Streamlit supplies the browser interface; GenAI Studio supplies model access and, if chosen, reference retrieval. The interface collects a request, calls Python on the server, and displays a draft. Saved case data belongs in a database or private file store when it must outlive the browser session.

For a résumé translation application, build a small review screen first: source text, a deliberate generate action, a draft clearly associated with that source, and an export. Add structured profiles and passage evidence as those components become reliable.

Browser formPython on hostGenAI Studio requestDraft in sessionReview + durable save

Keep ingestion out of screen reruns

The knowledge-base guide creates a collection in a separate script. The web application reads its saved ID. Uploading the reference library or creating a collection every time a screen reruns would duplicate work and can create many collections.

Similarly, importing crosswalk CSVs should be a versioned ingestion operation. Display controls should query existing records. A widget change is not a reason to regenerate a résumé or rebuild a vector index.

Configure the server

Install the SDK using the access guide, then add Streamlit in the same environment:

BASH · Example 01
python -m pip install streamlit
streamlit run app.py

Configure GENAI_STUDIO_API_KEY and GENAI_STUDIO_MODEL on the application server. Set GENAI_STUDIO_KB_ID only after the intended reference collection is ready. The example reads environment variables directly. For Community Cloud, top-level entries in its secrets configuration are exposed as environment variables; verify settings in the actual deployment.

Code and configuration are different things. Keep a reproducible dependency specification in the project, and configure secret values in the host's secrets system. A browser should not receive the gateway API key. The collection ID alone does not grant access.

A complete small Streamlit example

Save this code as app.py. It accepts pasted text rather than a PDF so that the first experiment isolates the model/UI connection from document parsing. Use synthetic inputs until the application's case access and storage design are ready. This is a teaching prototype, not a complete multiuser résumé system.

PYTHON · Example 02
import os
from datetime import datetime, timezone
import streamlit as st
from genai_studio import GenAIStudio

st.set_page_config(page_title="Résumé Translation Prototype")
st.title("Review a civilian résumé draft")
st.caption("Start with a synthetic example. Review every factual claim.")

required = ["GENAI_STUDIO_API_KEY", "GENAI_STUDIO_MODEL"]
missing = [name for name in required if not os.environ.get(name)]
if missing:
    st.error("Configure the server settings: " + ", ".join(missing))
    st.stop()

kb_id = os.environ.get("GENAI_STUDIO_KB_ID", "").strip()
if "draft_result" not in st.session_state:
    st.session_state.draft_result = None

with st.form("translation_request"):
    source = st.text_area(
        "Source text",
        value="Maintained logs and coordinated repairs for 12 vehicles.",
    )
    use_references = st.checkbox(
        "Use the configured reference library",
        value=False,
        disabled=not bool(kb_id),
    )
    submitted = st.form_submit_button("Generate draft")

if submitted:
    st.session_state.draft_result = None
    if not source.strip():
        st.warning("Enter source text first.")
    else:
        with st.spinner("Preparing a draft..."):
            try:
                ai = GenAIStudio()
                response = ai.chat_complete(
                    "Write one civilian résumé bullet from this source:\n" + source,
                    model=os.environ["GENAI_STUDIO_MODEL"],
                    collections=[kb_id] if use_references else None,
                    system=(
                        "Preserve only facts supported by the supplied source. "
                        "Use references for terminology, not to invent experience. "
                        "Do not add credentials, seniority, or outcomes."
                    ),
                )
                if not response.content.strip():
                    raise RuntimeError("Empty response")
                st.session_state.draft_result = {
                    "source": source,
                    "draft": response.content,
                    "model": response.model,
                    "created": datetime.now(timezone.utc).isoformat(),
                }
            except Exception:
                st.error("Generation failed. Check the server connection and settings.")

record = st.session_state.draft_result
if record:
    st.subheader("Source used for this draft")
    st.text(record["source"])
    st.subheader("Generated draft — review required")
    st.write(record["draft"])
    st.caption("Generated at " + record["created"])
    st.download_button(
        "Download draft",
        data=record["draft"],
        file_name="civilian-draft.txt",
        mime="text/plain",
    )

The form batches inputs behind a submit action. The model call runs only when submitted is true. Session state retains the previous completed result during ordinary reruns. The saved source is displayed beside its draft so a later edit to the input field is not silently presented as the source of an older answer. A new submission clears the old result before attempting generation.

The exception message avoids dumping raw gateway responses into the interface. Add controlled server diagnostics with a run ID when building the actual application. Preserve the error category without exposing keys or raw private documents in general logs.

Extend the prototype

Prototype behavior Next implementation step
Pasted source text Add PDF extraction and inspection using a chosen parser.
Generated free-text bullet Add the structured extraction stage and evidence checks.
Optional built-in RAG Inspect and display source passages; verify that they support the answer.
Browser-session result Add authenticated case records and durable database writes.
Draft download Add reviewer edits, accepted versions, and a stable export format.
One synchronous request Add a job record and worker if measured processing time requires it.

Session state is not durable storage. It can be lost when the session ends, reconnects, or the app restarts. Store important case data and review decisions in Postgres or another deliberate persistence layer. Shared caches can cross user sessions, so do not use a general cache as private case storage.

A form reduces accidental calls caused by widget changes, but does not provide complete job deduplication. For longer jobs, generate a job ID, record queued/running/completed/failed status, and have a worker process it. Repeated submissions should follow an explicit retry or new-version policy.

Where Railway, Fly.io, and Community Cloud fit

Streamlit is the interface framework. Community Cloud is a host for that framework; Railway and Fly.io can host Streamlit too. Changing hosts does not automatically require replacing the UI with React.

Use Community Cloud for a small shared demonstration if its limits and connectivity fit. Railway is a reasonable next choice when the app needs distinct UI/API/worker services. Fly.io fits when a team member can manage the additional Machine/container configuration for a concrete requirement. Compare the current Railway planshttps://docs.railway.com/pricing/plans, Fly.io resource pricinghttps://fly.io/docs/about/pricing/, and Community Cloud documentationhttps://docs.streamlit.io/deploy/streamlit-community-cloud. Check persistence, resource limits, account ownership, and operational responsibilities for the chosen host.

Regardless of host, verify an authorized synthetic model request from that host before committing. A laptop's Purdue VPN access does not extend automatically to the cloud server. If campus access is required, evaluate an institutional host or approved network arrangement.

Use streaming only when the state handling is clear

The SDK's chat_stream() can drive an incremental display. Store the complete result only after the stream finishes, and distinguish a partial response from a completed draft after an error. Streaming improves perceived responsiveness; it does not make the content more trustworthy. Begin with chat_complete() while measuring correctness and request failures.

Conversation history can be stored per session for follow-up editing, but verified profile data should still be an explicit record. When switching cases, clear or replace the relevant session history so one veteran's facts do not influence another's draft.

Practical checks

Show that changing an input does not call the model until submission. Generate a synthetic draft, download it, then demonstrate an empty input and a simulated failure. Confirm that a new request is not confused with a stale result. Before using real cases, show how two users' records remain separate and how saved edits survive a restart.

Further reading