Super-frustrated with Streamlit...can you create real apps with this?

I’m using AI to generate my streamlit code and it’s struggling. It is struggling with basic concepts.

I’m creating an app which is an AI assistant. My goal is to open-source this app for others to use.

My first AI assistant is called “Course Machina” which creates course transcripts from slides uploaded. It calls APIs to claude to generate transcripts.

I have a simple screen with slides and multiple text area boxes to add talking points and view the transcripts. The transcripts are running but I can’t get the screen to refresh with the updated transcript.

Is streamlit designed to create REAL applications? It seems like this shouldn’t be this complex.

Hello,
AI is not magic :slight_smile:
What is your code ? Give an example of your problem, a screenshoot, a video …
Here we just can say “Yes, it can be design to real application” (I’m using it daily for my work).

Thanks for your reply! AI has usually been magic for me (doing mostly python automation scripting) and I almost never code anything by hand. I have found Claude to be the best one, ChatGPT gets lazy after a while, and Gemini is the laziest one that I only use when I have an error that neither Claude nor ChatGPT can resolve. It often can provide guidance but never the full code.

Specifically I am rendering a list of slides and talking points, calling an API that calls a Claude api to generate a transcript and then I want to re-render the updated transcript in the screen.

It’s either re-rendering the transcript and getting a “duplicate widget id” error, or not re-rendering at all.

Here’s the code current code (getting a dupliicate widget id error:

import streamlit as st
from services.dreamfactory_service import dreamfactory_service
from app import load_css
import asyncio
import aiohttp
from dotenv import load_dotenv
import os

# Load environment variables from .env file
load_dotenv()

# Get the FastAPI URL from environment variables
FASTAPI_URL = os.getenv('FASTAPI_URL')


async def generate_transcript(image_url, talking_points):
    """
    Asynchronously sends a POST request to generate a transcript using the given image URL and talking points.
    """
    if not talking_points:
        return {"error": "Talking points are empty"}

    # Debug print
    print(f"Sending request with talking points: {talking_points}")

    payload = {
        'image_url': image_url,
        'talking_points': talking_points
    }

    # Debug print
    print(f"Full payload: {payload}")

    async with aiohttp.ClientSession() as session:
        async with session.post(f'{FASTAPI_URL}/claude/generate_transcript', json=payload) as response:
            if response.status == 200:
                return await response.json()
            else:
                error_text = await response.text()
                # Debug print
                print(f"Error response: {error_text}")
                return {"error": f"Failed to generate transcript: {error_text}"}


def fetch_module_data(module_id):
    """
    Fetches module data from the DreamFactory service using the module ID.
    """
    module_data = dreamfactory_service.get_data('modules', filter=f"id={module_id}")
    return module_data['resource'][0] if module_data['resource'] else None


def fetch_slides_data(module_id):
    """
    Fetches slides data associated with a given module ID.
    """
    slides_data = dreamfactory_service.get_data('slides', filter=f"module_id={module_id}", order="display_order")
    return slides_data['resource'] if slides_data['resource'] else []


def fetch_videos_data(slide_id):
    """
    Fetches video data associated with a given slide ID.
    """
    videos_data = dreamfactory_service.get_data('slide_videos', filter=f"slide_id={slide_id}")
    return videos_data['resource'] if videos_data['resource'] else []


def save_module(module_id, description):
    """
    Saves the module's description to the DreamFactory service.
    """
    updated_module = {
        'id': module_id,
        'description': description
    }
    result = dreamfactory_service.create_or_update('modules', updated_module)
    return result is not None


def save_slide(slide):
    """
    Saves slide data to the DreamFactory service.
    """
    result = dreamfactory_service.create_or_update('slides', slide)
    return result is not None


def display_module_description(module):
    """
    Displays the module description in a text area and returns the entered text.
    """
    return st.text_area("Module Description", value=module['description'], key="module_description")


def display_slide(slide, index):
    st.subheader(f"Slide {index + 1}: {slide['title']}")

    col1, col2 = st.columns(2)

    with col1:
        st.image(slide['image_link'], use_column_width=True)

    with col2:
        talking_points_key = f"tp_{slide['id']}"
        transcript_key = f"tr_{slide['id']}"

        # Initialize session state
        if talking_points_key not in st.session_state:
            st.session_state[talking_points_key] = slide.get('talking_points', '')
        if transcript_key not in st.session_state:
            st.session_state[transcript_key] = slide.get('transcript', '')

        # Create text areas
        talking_points = st.text_area("Talking Points", key=f"tp_input_{slide['id']}",
                     value=st.session_state[talking_points_key], height=200)

        # Create an empty container for the transcript
        transcript_container = st.empty()

        # Update the transcript container
        transcript = transcript_container.text_area("Transcript", key=f"tr_input_{slide['id']}",
                     value=st.session_state[transcript_key], height=200)

        # Update session state based on current values
        st.session_state[talking_points_key] = talking_points
        st.session_state[transcript_key] = transcript

        col_gen, col_save = st.columns(2)

        with col_gen:
            if st.button("Generate Transcript", key=f"gen_button_{slide['id']}"):
                generate_transcript_callback(slide['image_link'], talking_points_key, transcript_key, slide['id'], transcript_container)

        with col_save:
            if st.button("Save Slide", key=f"save_button_{slide['id']}"):
                save_slide_callback(slide['id'], talking_points_key, transcript_key)

    # Display videos if available
    videos = fetch_videos_data(slide['id'])
    if videos:
        st.subheader("Videos")
        for video in videos:
            st.video(video['video_link'])
            st.audio(video['audio_link'])

def generate_transcript_callback(image_url, talking_points_key, transcript_key, slide_id, transcript_container):
    with st.spinner("Generating transcript..."):
        result = asyncio.run(generate_transcript(image_url, st.session_state[talking_points_key]))
        if result and 'transcript' in result:
            st.session_state[transcript_key] = result['transcript']
            save_slide({
                'id': slide_id,
                'transcript': st.session_state[transcript_key],
                'talking_points': st.session_state[talking_points_key]
            })
            # Update the transcript container with the new content
            transcript_container.text_area("Transcript", key=f"tr_input_{slide_id}",
                                           value=st.session_state[transcript_key], height=200)
            st.success("Transcript generated and saved successfully!")
        else:
            st.error(f"Failed to generate transcript: {result.get('error', 'Unknown error')}")


def save_slide_callback(slide_id, talking_points_key, transcript_key):
    updated_slide = {
        'id': slide_id,
        'talking_points': st.session_state[talking_points_key],
        'transcript': st.session_state[transcript_key]
    }
    if save_slide(updated_slide):
        st.success("Slide saved successfully!")
    else:
        st.error("Failed to save slide.")



async def call_generate_transcript(image_url, talking_points, transcript_key, slide_id):
    """
    Calls the generate_transcript endpoint and updates the UI with the result.
    """
    result = await generate_transcript(image_url, talking_points)
    if result and 'transcript' in result:
        st.session_state[transcript_key] = result['transcript']
        save_slide({
            'id': slide_id,
            'transcript': st.session_state[transcript_key]
        })
        st.success("Transcript generated and saved successfully!")
    else:
        st.error("Failed to generate transcript. Please try again.")
        st.session_state[transcript_key] = ""


def module_edit():
    """
    Main function to edit a module and manage slides.
    """
    load_css()
    st.title("Edit Module")

    # Fetch the module ID from session state
    module_id = st.session_state.get('module_id')
    if not module_id:
        st.error("No module selected.")
        if st.button("Back to Course Sections"):
            st.switch_page("pages/course_sections.py")
        return

    # Fetch module data
    module = fetch_module_data(module_id)
    if not module:
        st.error("Module not found.")
        if st.button("Back to Course Sections"):
            st.switch_page("pages/course_sections.py")
        return

    # Display the module title
    st.header(f"Editing Module: {module['name']}")

    # Display module description
    description = display_module_description(module)

    col1, col2 = st.columns(2)
    with col1:
        if st.button("Save All Changes", key="save_all"):
            with st.spinner("Saving all changes..."):
                # Save module description and slides
                module_saved = save_module(module_id, description)
                slides = fetch_slides_data(module_id)
                slides_saved = all(save_slide({
                    'id': slide['id'],
                    'talking_points': st.session_state.get(f"tp_{slide['id']}", ""),
                    'transcript': st.session_state.get(f"tr_{slide['id']}", "")
                }) for slide in slides)

                if module_saved and slides_saved:
                    st.success("All changes saved successfully!")
                else:
                    st.error("Failed to save some changes. Please try again.")

    with col2:
        if st.button("Generate All Transcripts", key="generate_all"):
            slides = fetch_slides_data(module_id)
            asyncio.run(generate_all_transcripts(slides))

    # Display each slide
    slides = fetch_slides_data(module_id)
    for i, slide in enumerate(slides):
        display_slide(slide, i)

    # Button to navigate back to the course sections
    if st.button("Back to Course Sections"):
        st.switch_page("pages/course_sections.py")

async def generate_all_transcripts(slides):
    for slide in slides:
        talking_points_key = f"tp_{slide['id']}"
        transcript_key = f"tr_{slide['id']}"
        if not st.session_state[transcript_key]:  # Only generate if transcript is empty
            result = await generate_transcript(slide['image_link'], st.session_state[talking_points_key])
            if result and 'transcript' in result:
                st.session_state[transcript_key] = result['transcript']
                # Save the generated transcript immediately
                save_slide({
                    'id': slide['id'],
                    'transcript': st.session_state[transcript_key]
                })
    st.success("All transcripts generated and saved successfully!")
    st.experimental_rerun()

module_edit()

Automation scripting and interactive web apps are very different beasts. How good are your AIs at coding non-streamlit web apps?

1 Like

Yeah, it’s working great for task where a lot of person already did the code. Streamlit is quite “new”, the model are not train on the new feature.
First, what version of streamlit are you ? I saw a st.experimental_rerun() it’s not this anymore since 1.26 : st.rerun().

dupliicate widget id error is because you got multiple widget with the same ID (each widget got a “key”). In the for loop if the same widget is create multiple times, it will create this error.

To fix this, you need to ensure you don’t have duplicate :

Example

st.button("Back to Course Sections")
st.button("Back to Course Sections", key="UNIQUEKEY")

hi @Faltawer - Just wanted to say thanks for this info. This was the issue!

Also…AI is pretty magical at generating code - this morning over the course of 2 hours I built a process that creates mp3 using aragon text-to-speech, creates an mp4 of a slide using an image and the mp3 and I updated maybe 20 lines of code.

It’s not perfect but I’m pretty happy about my copying/pasting/debugging life. I’ve found Claude.ai to be the best, ChatGPT second best (unfortunately it gets lazy and starts changing code that it doesn’t need to), an Gemini I only use to troubleshoot errors when the other two can’t figure it out.

Thanks again for your help!

1 Like

This topic was automatically closed 2 days after the last reply. New replies are no longer allowed.