{"id":19110070,"url":"https://github.com/tsuzukia21/streamlit-chatbot","last_synced_at":"2026-05-15T11:37:48.458Z","repository":{"id":270781453,"uuid":"804929996","full_name":"tsuzukia21/streamlit-chatbot","owner":"tsuzukia21","description":null,"archived":false,"fork":false,"pushed_at":"2026-02-21T20:45:37.000Z","size":566,"stargazers_count":0,"open_issues_count":0,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-02-21T22:59:10.181Z","etag":null,"topics":["streamlit","streamlit-webapp"],"latest_commit_sha":null,"homepage":"","language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tsuzukia21.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2024-05-23T14:45:52.000Z","updated_at":"2026-01-21T15:54:50.000Z","dependencies_parsed_at":"2025-01-03T03:45:36.589Z","dependency_job_id":"06e6f1fe-d4db-4c06-9c8d-f58bd7f8e20a","html_url":"https://github.com/tsuzukia21/streamlit-chatbot","commit_stats":null,"previous_names":["tsuzukia21/streamlit-chatbot"],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/tsuzukia21/streamlit-chatbot","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsuzukia21%2Fstreamlit-chatbot","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsuzukia21%2Fstreamlit-chatbot/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsuzukia21%2Fstreamlit-chatbot/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsuzukia21%2Fstreamlit-chatbot/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tsuzukia21","download_url":"https://codeload.github.com/tsuzukia21/streamlit-chatbot/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsuzukia21%2Fstreamlit-chatbot/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":33065720,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-15T11:35:32.926Z","status":"ssl_error","status_checked_at":"2026-05-15T11:35:31.362Z","response_time":103,"last_error":"SSL_connect returned=1 errno=0 peeraddr=140.82.121.5:443 state=error: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["streamlit","streamlit-webapp"],"created_at":"2024-11-09T04:23:31.317Z","updated_at":"2026-05-15T11:37:48.453Z","avatar_url":"https://github.com/tsuzukia21.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"## Streamlit Chatbot\n[English](./README_EN.md)\n\nマルチモーダル入力（テキスト/画像/音声）に対応した Streamlit 製チャットボットです。会話履歴は Firestore、画像は Cloud Storage に永続化します。モデル切替（Anthropic, Google, OpenAI）や思考可視化（thinking/chain-of-thoughtの表示）に対応しています。\n\n### 主な機能\n- **モデル切替**: Opus 4.6 / Gemini 3.1 / GPT‑5.4（LangChain ラッパー）\n- **ツール付与**: 各モデルにネット検索ツール付与。最新の話題にも対応\n- **マルチモーダル入力**: 画像アップロード、音声認識（Whisper）\n- **会話管理**: 新規作成、タイトル自動生成、編集（過去メッセージから再分岐）\n- **永続化**: Firestore（テキスト）、Cloud Storage（画像）\n- **思考可視化**: reasoning/thinking 表示\n- **認証**: `st.login()` を用いた Google ログイン（`secrets.toml` を Secret Manager でマウント）＋ `allowed_emails` によるメールホワイトリスト（任意）\n\n---\n\n## ファイル構成\n\n```\nstreamlit-chatbot/\n  main.py                     # Streamlit アプリ本体\n  core/\n    config.py                 # アプリ設定（システムプロンプト、トークン制限等）\n    MODEL_CONFIG.py           # モデル定義/LLMファクトリ\n    llm_handler.py            # LangChain チェーン構築とストリーミング\n    conversation.py           # 会話の新規/読込/削除、タイトル生成\n    database.py               # Firestore/Cloud Storage 永続化\n    ui_components.py          # メッセージ表示/編集、思考の折りたたみ等\n  requirements.txt            # pip 用依存\n  pyproject.toml, poetry.lock # Poetry 用依存\n  Dockerfile                  # Cloud Run 用コンテナ定義\n  .streamlit/\n    secrets.toml              # 認証/設定（本番は Secret Manager からマウント）\n  README.md                   # 本ファイル\n```\n\n---\n\n## 動作要件\n- Python\n- GCP: Firestore（ネイティブモード）、Cloud Storage バケット\n- API キー（必要に応じて）\n  - OpenAI: `OPENAI_API_KEY`\n  - Anthropic: `ANTHROPIC_API_KEY`\n  - Gemini: `GOOGLE_API_KEY`\n\n---\n\n## セットアップ（ローカル）\n\n1) 依存インストール（pip または Poetry）\n```bash\n# pip\npip install -r requirements.txt\n\n# または Poetry\npoetry install\n```\n\n2) `.streamlit/secrets.toml` を作成（ローカル実行時）\n```toml\n[auth]\nredirect_uri = \"http://localhost:8501/oauth2callback\"\ncookie_secret = \"[強力なランダム文字列]\"\nclient_id = \"[Google OAuth クライアントID]\"\nclient_secret = \"[Google OAuth クライアントシークレット]\"\nserver_metadata_url = \"https://accounts.google.com/.well-known/openid-configuration\"\nallowed_emails = [\"your-test-user@example.com\"] # 任意（メールホワイトリスト）。完全一致で判定\n```\n\n3) 環境変数（必要に応じて）\n```bash\nexport OPENAI_API_KEY=...\nexport ANTHROPIC_API_KEY=...\nexport GOOGLE_API_KEY=...\n\n# ローカルでサービスアカウント鍵を使う場合のみ\nexport GOOGLE_APPLICATION_CREDENTIALS=/abs/path/to/service-account.json\n```\n\n4) 実行\n```bash\nstreamlit run main.py\n```\n\n### アクセス制御（メールホワイトリスト、任意）\n- `secrets.toml` の `[auth].allowed_emails` にメールアドレスの配列を設定すると、ログイン後に `st.user.email` と完全一致で照合し、許可されていないユーザーは即時ブロック＋ログアウトします。\n- `allowed_emails` が未設定または空配列の場合は、このチェックはスキップされます（Google 側の OAuth 設定のみで制御）。\n- 実装箇所: `main.py` のサイドバー内、ログイン済み分岐直後。\n\n例（`secrets.toml`）:\n```toml\n[auth]\n...\nallowed_emails = [\"your-test-user@example.com\", \"another@example.com\"]\n```\n\n補足:\n- `st.login()`/`st.user`/`st.logout()` を用いた Google 認証の概要や `secrets.toml` 設定は、この記事がとても分かりやすいです。[【st.login】GoogleアカウントでログインできるStreamlitアプリの開発方法と仕組みをわかりやすく解説](https://zenn.dev/datum_studio/articles/c964f9e38379f4)）。\n- 画像アップロードや音声認識にはブラウザ権限が必要です。\n\n---\n\n## Secrets / 環境変数の取り扱い（本番）\n\n本番（Cloud Run）では `.streamlit/secrets.toml` を直接コミットせず、**Secret Manager に登録したシークレットをファイルとしてコンテナ内 `/app/.streamlit/secrets.toml` にマウント**します。\n\n### 手順（例）\n1) Secret Manager に登録\n```bash\ngcloud secrets create streamlit-secrets --replication-policy=automatic\ngcloud secrets versions add streamlit-secrets --data-file=.streamlit/secrets.toml\n```\n\n2) Cloud Run でファイルとしてマウント\n- Cloud Console の Cloud Run \u003e サービス \u003e 編集 \u003e セキュリティ \u003e シークレット から、\n  `streamlit-secrets` を「ファイルとしてマウント」に設定し、マウント先パスを `/app/.streamlit/secrets.toml` に指定\n- これによりアプリはローカルと同じパスで `secrets.toml` を参照します\n\n3) 追加の環境変数（必要に応じて）\n- `OPENAI_API_KEY`, `ANTHROPIC_API_KEY`, `GOOGLE_API_KEY` も Secret Manager で管理し、Cloud Run 環境変数として参照\n- LangSmithのセットアップもお勧めします\n\n---\n\n## GCP 設定\n\n### 1. プロジェクト準備\n- Firestore（ネイティブモード）を有効化（リージョンは Cloud Run/Storage と合わせる）\n- Cloud Storage バケット作成\n\n### 2. サービスアカウントと権限\nCloud Run 実行サービスアカウントに最低限以下のロールを付与:\n- `roles/datastore.user`（Firestore）\n- `roles/storage.objectAdmin`（Cloud Storage）\n- 推奨: `roles/logging.logWriter`\n\n### 3. デプロイ（Cloud Run）\nコンテナビルドとデプロイ例:\n```bash\n# Artifact Registry へビルド \u0026 プッシュ\ngcloud builds submit --tag REGION-docker.pkg.dev/PROJECT_ID/REPO/streamlit-chatbot:latest\n\n# デプロイ（シークレットのファイルマウントはコンソール/マニフェストで設定）\ngcloud run deploy streamlit-chatbot \\\n  --image REGION-docker.pkg.dev/PROJECT_ID/REPO/streamlit-chatbot:latest \\\n  --platform managed \\\n  --region REGION \\\n  --service-account YOUR_SA@PROJECT_ID.iam.gserviceaccount.com \\\n  --allow-unauthenticated \\\n  --set-env-vars OPENAI_API_KEY=...,ANTHROPIC_API_KEY=...,GOOGLE_API_KEY=...\n```\n\n補足:\n- 認証をパブリックにしたくない場合は `--no-allow-unauthenticated` とし、IAP などで保護\n- 画像が表示されない場合は `GCS_BUCKET_NAME` の設定/バケット権限/CORS を確認\n\n---\n\n## データモデル（永続化）\n- Firestore コレクション\n  - `conversations/{conversationId}`: `user_id`, `title`, `total_tokens`, `is_deleted`, `created_at`, `updated_at`\n  - `conversations/{conversationId}/messages/{messageId}`: `role`, `content(json)`, `reasoning`, `created_at`\n- Cloud Storage\n  - `images/conv{conversationId}_msg{messageId}_{index}.{ext}` に保存\n  - 保存時に data URI を GCS パスへ置換、読込時に data URI に復元\n\n---\n\n## Docker 実行\nDockerfile は Poetry を用いて依存を解決し、`PORT=8080` を Cloud Run 既定に合わせています。\n```bash\ndocker build -t streamlit-chatbot:local .\ndocker run -p 8080:8080 \\\n  -e OPENAI_API_KEY=... -e ANTHROPIC_API_KEY=... -e GOOGLE_API_KEY=... \\\n  streamlit-chatbot:local\n```\n\n---\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftsuzukia21%2Fstreamlit-chatbot","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftsuzukia21%2Fstreamlit-chatbot","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftsuzukia21%2Fstreamlit-chatbot/lists"}