Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 69 additions & 0 deletions .github/workflows/python-modernization.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Python modernization validation

on:
pull_request:
push:
branches:
- main
- agent/openai-responses-modernization-2026-v1

permissions:
contents: read

jobs:
validate:
name: validate (Python ${{ matrix.python-version }})
runs-on: ubuntu-latest

strategy:
fail-fast: false
matrix:
python-version:
- "3.10"
- "3.12"

steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
cache: pip

- name: Install dependencies
run: python -m pip install -r requirements.txt

- name: Compile Python
run: python -m compileall -q .

- name: Run offline unit tests
run: python -m unittest discover -s tests -v

- name: Reject secrets and legacy API in modern entrypoints
shell: bash
run: |
set -euo pipefail
if grep -R -n -E \
--exclude-dir=.git \
--exclude='*.ipynb' \
'sk-[A-Za-z0-9_-]{20,}' .; then
echo "Potential API key detected."
exit 1
fi
modern_files=(openai_service.py)

if [[ -f chat.py ]]; then
modern_files+=(chat.py)
fi

if [[ -f ChatGPT_Web_Application.py ]]; then
modern_files+=(ChatGPT_Web_Application.py)
fi

if grep -n -E 'openai\.Completion|text-davinci-003' \
"${modern_files[@]}"; then
echo "Legacy OpenAI API usage detected in a modern entrypoint."
exit 1
fi
6 changes: 6 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
.venv/
.env
__pycache__/
*.py[cod]
.pytest_cache/
.streamlit/secrets.toml
141 changes: 76 additions & 65 deletions ChatGPT_Web_Application.py
Original file line number Diff line number Diff line change
@@ -1,71 +1,82 @@
# import streamlit as st
from __future__ import annotations

# st.title("ChatGPT-like Web App")
# #storing the chat
# if 'generated' not in st.session_state:
# st.session_state['generated'] = []
# if 'past' not in st.session_state:
# st.session_state['past'] = []
# user_input=st.text_input("You:",key='input')
# if user_input:
# output=generate_response(user_input)
# #store the output
# st.session_state['past'].append(user_input)
# st.session_state['generated'].append(output)
# if st.session_state['generated']:
# for i in range(len(st.session_state['generated'])-1, -1, -1):
# message(st.session_state["generated"][i], key=str(i))
# message(st.session_state['past'][i], is_user=True, key=str(i) + '_user')
import os

import streamlit as st

from openai_service import DEFAULT_MODEL, generate_response

st.set_page_config(
page_title="ChatGPT-style Responses API Demo",
page_icon="💬",
)

import streamlit as st
st.title("ChatGPT-style Web Application")
st.caption("Modern OpenAI Responses API + Streamlit")

if "messages" not in st.session_state:
st.session_state.messages = []

if "previous_response_id" not in st.session_state:
st.session_state.previous_response_id = None

if "active_model" not in st.session_state:
st.session_state.active_model = DEFAULT_MODEL

with st.sidebar:
st.header("Settings")
model = st.text_input(
"Model",
value=st.session_state.active_model,
help="Default comes from OPENAI_MODEL or falls back to gpt-5.5.",
).strip()

if not model:
model = DEFAULT_MODEL

if model != st.session_state.active_model:
st.session_state.active_model = model
st.session_state.previous_response_id = None
st.session_state.messages = []
st.info("Conversation reset because the model changed.")

if st.button("Clear conversation", use_container_width=True):
st.session_state.messages = []
st.session_state.previous_response_id = None
st.rerun()

st.markdown("---")
if os.getenv("OPENAI_API_KEY"):
st.success("OPENAI_API_KEY detected.")
else:
st.warning("Set OPENAI_API_KEY in the environment before sending a message.")

for message in st.session_state.messages:
with st.chat_message(message["role"]):
st.markdown(message["content"])

prompt = st.chat_input("Ask something...")

if prompt:
st.session_state.messages.append({"role": "user", "content": prompt})

with st.chat_message("user"):
st.markdown(prompt)

try:
with st.chat_message("assistant"):
with st.spinner("Thinking..."):
answer, response_id = generate_response(
prompt,
model=st.session_state.active_model,
previous_response_id=st.session_state.previous_response_id,
)
st.markdown(answer)

st.session_state.messages.append(
{"role": "assistant", "content": answer}
)
st.session_state.previous_response_id = response_id

# this loop will let us ask questions continuously

while True:

# Set up the model and prompt
model_engine = "text-davinci-003"

prompt = input('Enter new prompt: ')

if 'exit' in prompt or 'quit' in prompt:
break

# Generate a response
# given the most recent context (4096 characters)
# continue the text up to 2048 tokens ~ 8192 charaters
completion = openai.Completion.create(
engine=model_engine,
prompt=prompt,
max_tokens=1024,
n=1,
stop=None,
temperature=0.5,
)

# extracting useful part of response
response = completion.choices[0].text

# printing response
print(response)


st.title("ChatGPT-like Web App")
#storing the chat
if 'generated' not in st.session_state:
st.session_state['generated'] = []
if 'past' not in st.session_state:
st.session_state['past'] = []
user_input=st.text_input("You:",key='input')
if user_input:
output=generate_response(user_input)
#store the output
st.session_state['past'].append(user_input)
st.session_state['generated'].append(output)
if st.session_state['generated']:
for i in range(len(st.session_state['generated'])-1, -1, -1):
message(st.session_state["generated"][i], key=str(i))
message(st.session_state['past'][i], is_user=True, key=str(i) + '_user')
except Exception as exc:
st.error(f"OpenAI request failed: {type(exc).__name__}: {exc}")
126 changes: 98 additions & 28 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,52 +1,122 @@
# ChatGPT Web Application — Streamlit & Gradio Experiments
# ChatGPT Web Application — Modern Responses API + Legacy Experiments

[![GitHub stars](https://img.shields.io/github/stars/AmirMotefaker/ChatGPT-Web-Application?style=flat&logo=github)](https://github.com/AmirMotefaker/ChatGPT-Web-Application/stargazers)
[![GitHub forks](https://img.shields.io/github/forks/AmirMotefaker/ChatGPT-Web-Application?style=flat&logo=github)](https://github.com/AmirMotefaker/ChatGPT-Web-Application/network/members)
[![Python](https://img.shields.io/badge/Python-Streamlit%20%2B%20Gradio-3776AB?logo=python&logoColor=white)](https://www.python.org/)
[![Python modernization](https://github.com/AmirMotefaker/ChatGPT-Web-Application/actions/workflows/python-modernization.yml/badge.svg)](https://github.com/AmirMotefaker/ChatGPT-Web-Application/actions/workflows/python-modernization.yml)

An educational archive of early Python experiments for building ChatGPT-style interfaces with Streamlit, Gradio, Jupyter, Colab, and Kaggle.
A runnable Streamlit chat application using the modern OpenAI Responses API, while preserving the project's original Colab/Kaggle/Gradio notebooks as historical learning material.

> [!IMPORTANT]
> **Legacy educational project.** The current Python example uses `text-davinci-003` and `openai.Completion.create`, which belong to an older OpenAI API generation. The repository is valuable as a historical learning project, but the code should be modernized before being treated as a current production template.

## Repository contents
## Modern 2026 application

| File | Purpose |
| --- | --- |
| [`ChatGPT_Web_Application.py`](ChatGPT_Web_Application.py) | Python/Streamlit experiment |
| [`ChatGPT_Web_Application_colab.ipynb`](ChatGPT_Web_Application_colab.ipynb) | Colab notebook |
| [`ChatGPT_Web_Application_using_Streamlit_colab.ipynb`](ChatGPT_Web_Application_using_Streamlit_colab.ipynb) | Streamlit-focused Colab example |
| [`ChatGPT_Web_Application_using_Gradio_colab.ipynb`](ChatGPT_Web_Application_using_Gradio_colab.ipynb) | Gradio-focused Colab example |
| [`chatgpt-web-application-kaggle.ipynb`](chatgpt-web-application-kaggle.ipynb) | Kaggle notebook |
| [`chatgpt-web-application-using-gradio.ipynb`](chatgpt-web-application-using-gradio.ipynb) | Gradio notebook |
[`ChatGPT_Web_Application.py`](ChatGPT_Web_Application.py) is now the supported application entrypoint.

## What the project demonstrates
The app uses:

- Prompt/response experimentation in Python
- Early OpenAI Completion API usage
- Streamlit interface concepts
- Gradio-based notebook experiments
- Jupyter/Colab/Kaggle workflows
- the official OpenAI Python SDK
- `client.responses.create(...)`
- `response.output_text`
- `OPENAI_API_KEY` from the environment
- `previous_response_id` for multi-turn conversation state
- Streamlit chat components
- `gpt-5.5` as the default model, overridable with `OPENAI_MODEL` or the sidebar

## Quick start for code review
### Setup

```bash
git clone https://github.com/AmirMotefaker/ChatGPT-Web-Application.git
cd ChatGPT-Web-Application
python -m venv .venv
```

Activate it:

```powershell
# Windows PowerShell
.venv\Scripts\Activate.ps1
```

```bash
# macOS / Linux
source .venv/bin/activate
```

Install dependencies:

```bash
python -m pip install -r requirements.txt
```

Set the API key locally:

```powershell
$env:OPENAI_API_KEY = "your-key-here"
```

or:

```bash
export OPENAI_API_KEY="your-key-here"
```

Review the notebooks and Python file before executing them. The API calls and model identifiers are legacy and may require modernization.
Run the app:

```bash
streamlit run ChatGPT_Web_Application.py
```

The sidebar lets you change the model and clear the current conversation. Changing the model resets the stored response chain to avoid mixing conversation state across models.

## Architecture

```text
Streamlit UI
|
v
ChatGPT_Web_Application.py
|
v
openai_service.py
|
v
OpenAI Responses API
```

`openai_service.py` isolates the API call from the UI and makes the Responses API contract testable without making paid network requests.

## Historical notebooks

These files are preserved as an archive of the original project:

| File | Status |
| --- | --- |
| `ChatGPT_Web_Application_colab.ipynb` | Legacy educational notebook |
| `ChatGPT_Web_Application_using_Streamlit_colab.ipynb` | Legacy Streamlit/Colab notebook |
| `ChatGPT_Web_Application_using_Gradio_colab.ipynb` | Legacy Gradio/Colab notebook |
| `chatgpt-web-application-kaggle.ipynb` | Legacy Kaggle notebook |
| `chatgpt-web-application-using-gradio.ipynb` | Legacy Gradio notebook |

> [!IMPORTANT]
> The historical notebooks can contain old model names or deprecated OpenAI API patterns. The root Streamlit application is the modern supported path.

## Validation

```bash
python -m unittest discover -s tests -v
python -m compileall -q .
```

> [!CAUTION]
> Do not hard-code or commit API credentials. Keep secrets outside the repository.
GitHub Actions validates the modernization on Python 3.10 and 3.12 and performs offline unit tests, syntax compilation, secret-pattern scanning, and a guard against legacy API usage in modern entrypoints.

## Modernization roadmap
## Security

The next code milestone should update the API integration, dependency management, secret handling, runnable examples, and basic validation. Keeping that work separate makes the repository history clear: **README modernization first, code modernization second.**
- Never hard-code an API key.
- Use `OPENAI_API_KEY` in your environment or secret manager.
- `.env` and Streamlit secret files are ignored.
- CI does not make live OpenAI API requests.

## Support the project

If these early Streamlit/Gradio experiments are useful as a learning reference, consider giving the repository a ⭐.
If the modern Streamlit example or legacy learning material helps you, consider giving the repository a ⭐.

## Author

Expand Down
Loading
Loading