Initial commit
This commit is contained in:
2
.env.chatbot
Normal file
2
.env.chatbot
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
# Replace with your actual Google API key
|
||||||
|
GOOGLE_API_KEY="AIzaSyD37Fp00b_i2DIywwtQu39w0RhkGAJO4YM"
|
||||||
32
.gitattributes
vendored
Normal file
32
.gitattributes
vendored
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
*.7z filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.arrow filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.bin filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.bz2 filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.ftz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.gz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.h5 filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.joblib filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.lfs.* filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.model filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.msgpack filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.npy filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.npz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.onnx filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.ot filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.parquet filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.pickle filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.pkl filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.pb filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.pt filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.pth filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.rar filter=lfs diff=lfs merge=lfs -text
|
||||||
|
saved_model/**/* filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.tar.* filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.tflite filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.tgz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.wasm filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.xz filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.zip filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.zst filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*tfevents* filter=lfs diff=lfs merge=lfs -text
|
||||||
|
*.pdf filter=lfs diff=lfs merge=lfs -text
|
||||||
23
Dockerfile
Normal file
23
Dockerfile
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
# Use an official Python runtime as a parent image
|
||||||
|
FROM python:3.9-slim
|
||||||
|
|
||||||
|
# Set the working directory in the container
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# Copy the requirements file into the container at /app
|
||||||
|
COPY requirements.txt .
|
||||||
|
|
||||||
|
# Install any needed packages specified in requirements.txt
|
||||||
|
RUN pip install --no-cache-dir -r requirements.txt
|
||||||
|
|
||||||
|
# Copy the rest of the application's code into the container at /app
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Make port 8501 available to the world outside this container
|
||||||
|
EXPOSE 8501
|
||||||
|
|
||||||
|
# Define environment variable
|
||||||
|
ENV GOOGLE_API_KEY=""
|
||||||
|
|
||||||
|
# Run main.py when the container launches
|
||||||
|
CMD ["streamlit", "run", "main.py"]
|
||||||
21
LICENSE
Normal file
21
LICENSE
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
MIT License
|
||||||
|
|
||||||
|
Copyright (c) 2024 Lectom
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
of this software and associated documentation files (the "Software"), to deal
|
||||||
|
in the Software without restriction, including without limitation the rights
|
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
copies of the Software, and to permit persons to whom the Software is
|
||||||
|
furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all
|
||||||
|
copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
SOFTWARE.
|
||||||
79
README.md
Normal file
79
README.md
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
# 🤖 OpenAPI 명세 조회 챗봇 (LLM-Gateway Spec Chatbot)
|
||||||
|
|
||||||
|
이 프로젝트는 특정 URL에서 제공하는 OpenAPI 명세(Specification) 파일을 기반으로 사용자의 질문에 답변하는 Streamlit 기반의 챗봇 애플리케이션입니다. Gemini AI 모델을 활용하여 자연어 질문을 이해하고, API 명세에 근거한 정확한 정보를 한국어로 제공합니다.
|
||||||
|
|
||||||
|
전체 애플리케이션은 Docker 컨테이너 환경에서 실행되도록 구성되어 있어, 간편하게 설치하고 실행할 수 있습니다.
|
||||||
|
|
||||||
|
## ✨ 주요 기능
|
||||||
|
|
||||||
|
- **OpenAPI 명세 기반 답변**: 로컬에 저장된 `api_spec.json` 파일을 분석하여 API의 엔드포인트, 파라미터, 요약 정보 등에 대해 답변합니다.
|
||||||
|
- **자연어 질의응답**: Google의 Gemini 모델을 통해 사용자의 자연어 질문을 이해하고 지능적인 답변을 생성합니다.
|
||||||
|
- **동적 명세 새로고침**: UI의 버튼 클릭 한 번으로 최신 API 명세를 원격 URL에서 다시 가져와 앱에 반영할 수 있습니다.
|
||||||
|
- **한국어 지원**: 모든 답변은 한국어로 제공됩니다.
|
||||||
|
- **직관적인 UI**: Streamlit을 사용하여 사용하기 쉬운 웹 기반 채팅 인터페이스를 제공하며, <20><><EFBFBD>고 및 커스텀 스타일이 적용되어 있습니다.
|
||||||
|
- **컨테이너화**: Docker 및 Docker Compose를 사용하여 개발 환경에 구애받지 않고 일관된 실행 환경을 제공합니다.
|
||||||
|
|
||||||
|
## 🛠️ 기술 스택
|
||||||
|
|
||||||
|
- **언어**: Python 3.9
|
||||||
|
- **프레임워크**: Streamlit
|
||||||
|
- **AI 모델**: Google Gemini 1.5 Flash
|
||||||
|
- **컨테이너**: Docker, Docker Compose
|
||||||
|
- **주요 라이브러리**: `google-generativeai`, `requests`, `pandas`
|
||||||
|
|
||||||
|
## 📂 디렉토리 구조
|
||||||
|
|
||||||
|
```
|
||||||
|
chatbot_app/
|
||||||
|
├── static/
|
||||||
|
│ ├── api_spec.json # API 명세 파일
|
||||||
|
│ └── logo.png # UI에 표시될 로고 이미지
|
||||||
|
├── .env.chatbot # 환경 변수 파일 (API 키 저장)
|
||||||
|
├── docker-compose.yml # Docker Compose 설정 파일
|
||||||
|
├── Dockerfile # Docker 이미지 빌드 파일
|
||||||
|
├── main.py # Streamlit 애플리케이션 메인 코드
|
||||||
|
├── README.md # 프로젝트 설명 파일 (현재 파일)
|
||||||
|
└── requirements.txt # Python 의존성 목록
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🚀 설치 및 실행 방법
|
||||||
|
|
||||||
|
### 사전 준비 사항
|
||||||
|
|
||||||
|
- [Docker](https://www.docker.com/get-started)가 설치되어 있어야 합니다.
|
||||||
|
- [Docker Compose](https://docs.docker.com/compose/install/)가 설치되어 있어야 합니다. (최신 버전의 Docker Desktop에는 기본 포함)
|
||||||
|
|
||||||
|
### 설치 절차
|
||||||
|
|
||||||
|
1. **프로젝트 준비**:
|
||||||
|
이 `chatbot_app` 디렉토리를 준비합니다.
|
||||||
|
|
||||||
|
2. **정적 파일 배치**:
|
||||||
|
`chatbot_app/static/` 디렉토리 안에 다음 두 파일을 위치시킵니다.
|
||||||
|
- `api_spec.json`: 조회할 대상의 OpenAPI 명세 파일
|
||||||
|
- `logo.png`: UI 사이드바에 표시할 로고 이미지
|
||||||
|
|
||||||
|
3. **환경 변수 설정**:
|
||||||
|
`chatbot_app/.env.chatbot` 파일을 열고, `YOUR_GEMINI_API_KEY` 부분을 실제 발급받은 Google Gemini API 키로 교체합니다.
|
||||||
|
```env
|
||||||
|
# YOUR_GEMINI_API_KEY를 실제 키로 변경하세요.
|
||||||
|
GOOGLE_API_KEY="YOUR_GEMINI_API_KEY"
|
||||||
|
```
|
||||||
|
|
||||||
|
4. **애플리케이션 빌드 및 실행**:
|
||||||
|
터미널에서 `chatbot_app` 디렉토리로 이동한 후, 다음 명령어를 실행합니다.
|
||||||
|
```bash
|
||||||
|
docker-compose up --build -d
|
||||||
|
```
|
||||||
|
- `--build`: 이미지를 새로 빌드합니다. (코드 변경 시 필요)
|
||||||
|
- `-d`: 컨테이너를 백그라운드에서 실행합니다.
|
||||||
|
|
||||||
|
5. **챗봇 접속**:
|
||||||
|
빌드가 완료되고 컨테이너가 실행되면, 웹 브라우저를 열고 다음 주소로 접속합니다.
|
||||||
|
- **URL**: `http://localhost:8501`
|
||||||
|
|
||||||
|
## 💡 사용 방법
|
||||||
|
|
||||||
|
- **질문하기**: 화면 하단의 입력창에 API 명세와 관련된 질문을 입력하고 Enter 키를 누릅니다.
|
||||||
|
- **API 명세 새로고침**: 사이드바의 '🔄 API 명세 새로고침' 버튼을 클릭하면 `API_SPEC_URL`에 지정된 주소에서 최신 명세를 다시 가져옵니다.
|
||||||
|
- **API 상세정보 확인**: 사이드바의 '📘 API 상세정보 보기'를 펼치면 API의 제목, 버전 및 전체 엔드포인트 목록을 확인할 수 있습니다.
|
||||||
10
docker-compose.yml
Normal file
10
docker-compose.yml
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
services:
|
||||||
|
chatbot:
|
||||||
|
build: .
|
||||||
|
container_name: pgn_spec_chatbot
|
||||||
|
ports:
|
||||||
|
- "8501:8501"
|
||||||
|
env_file:
|
||||||
|
- .env.chatbot
|
||||||
|
volumes:
|
||||||
|
- .:/app
|
||||||
197
main.py
Normal file
197
main.py
Normal file
@@ -0,0 +1,197 @@
|
|||||||
|
import json
|
||||||
|
import os
|
||||||
|
|
||||||
|
import google.generativeai as genai
|
||||||
|
import pandas as pd
|
||||||
|
import requests
|
||||||
|
import streamlit as st
|
||||||
|
|
||||||
|
# --- 설정 ---
|
||||||
|
SPEC_FILE = "static/api_spec.json"
|
||||||
|
LOGO_FILE = "static/logo.png"
|
||||||
|
API_SPEC_URL = "http://172.16.10.176:8888/openapi.json"
|
||||||
|
|
||||||
|
|
||||||
|
# --- 도우미 함수 ---
|
||||||
|
def load_api_spec():
|
||||||
|
"""로컬 파일에서 API 명세를 불러옵니다."""
|
||||||
|
try:
|
||||||
|
with open(SPEC_FILE, "r", encoding="utf-8") as f:
|
||||||
|
return json.load(f)
|
||||||
|
except (FileNotFoundError, json.JSONDecodeError) as e:
|
||||||
|
st.error(f"API 명세 파일을 불러오는 중 오류 발생: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
def fetch_and_save_spec():
|
||||||
|
"""URL에서 최신 API 명세를 가져와 로컬에 저장합니다."""
|
||||||
|
try:
|
||||||
|
response = requests.get(API_SPEC_URL)
|
||||||
|
response.raise_for_status() # 잘못된 상태 코드에 대해 예외를 발생시킵니다
|
||||||
|
spec_data = response.json()
|
||||||
|
with open(SPEC_FILE, "w", encoding="utf-8") as f:
|
||||||
|
json.dump(spec_data, f, indent=2, ensure_ascii=False)
|
||||||
|
st.success(f"성공적으로 API 명세를 가져와 업데이트했습니다: {API_SPEC_URL}")
|
||||||
|
return spec_data
|
||||||
|
except requests.exceptions.RequestException as e:
|
||||||
|
st.error(f"API 명세를 가져오는 데 실패했습니다: {e}")
|
||||||
|
return None
|
||||||
|
except json.JSONDecodeError:
|
||||||
|
st.error("응답에서 JSON을 파싱하는 데 실패했습니다.")
|
||||||
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
# --- Gemini AI 설정 ---
|
||||||
|
try:
|
||||||
|
api_key = os.getenv("GOOGLE_API_KEY")
|
||||||
|
if not api_key:
|
||||||
|
st.error(
|
||||||
|
"GOOGLE_API_KEY 환경 변수가 설정되지 않았습니다. .env.chatbot 파일에서 설정해주세요."
|
||||||
|
)
|
||||||
|
st.stop()
|
||||||
|
genai.configure(api_key=api_key)
|
||||||
|
model = genai.GenerativeModel("gemini-1.5-flash")
|
||||||
|
except Exception as e:
|
||||||
|
st.error(f"Gemini AI 설정 실패: {e}")
|
||||||
|
st.stop()
|
||||||
|
|
||||||
|
# --- Streamlit UI 설정 ---
|
||||||
|
st.set_page_config(
|
||||||
|
page_title="API Guide Chatbot",
|
||||||
|
page_icon="🤖",
|
||||||
|
layout="wide",
|
||||||
|
initial_sidebar_state="expanded",
|
||||||
|
)
|
||||||
|
|
||||||
|
# --- 커스텀 CSS ---
|
||||||
|
st.markdown(
|
||||||
|
"""
|
||||||
|
<style>
|
||||||
|
/* 다크 모드 기본 테마와 유사하게 맞춤 */
|
||||||
|
.stApp {
|
||||||
|
background-color: #0E117;
|
||||||
|
}
|
||||||
|
/* 채팅 메시지 스타일 */
|
||||||
|
[data-testid="chat-message-container"] {
|
||||||
|
border-radius: 0.5rem;
|
||||||
|
padding: 1rem;
|
||||||
|
margin-bottom: 1rem;
|
||||||
|
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
""",
|
||||||
|
unsafe_allow_html=True,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# --- 메인 앱 로직 ---
|
||||||
|
# 시작 시 API 명세 불러오기
|
||||||
|
api_spec = load_api_spec()
|
||||||
|
|
||||||
|
# --- 사이드바 ---
|
||||||
|
with st.sidebar:
|
||||||
|
if os.path.exists(LOGO_FILE):
|
||||||
|
st.image(LOGO_FILE, use_container_width=True)
|
||||||
|
st.header("⚙️ Controls")
|
||||||
|
if st.button("🔄 API 명세 새로고침"):
|
||||||
|
with st.spinner("최신 API 명세를 가져오는 중..."):
|
||||||
|
api_spec = fetch_and_save_spec()
|
||||||
|
st.session_state.messages = [] # 새로고침 시 대화 기록 삭제
|
||||||
|
st.rerun()
|
||||||
|
|
||||||
|
if api_spec:
|
||||||
|
with st.expander("📘 API 상세정보 보기", expanded=True):
|
||||||
|
st.info(f"**Title:** {api_spec.get('info', {}).get('title', 'N/A')}")
|
||||||
|
st.text(f"Version: {api_spec.get('info', {}).get('version', 'N/A')}")
|
||||||
|
|
||||||
|
paths = api_spec.get("paths", {})
|
||||||
|
if paths:
|
||||||
|
endpoint_data = []
|
||||||
|
for path, methods in paths.items():
|
||||||
|
for method, details in methods.items():
|
||||||
|
endpoint_data.append(
|
||||||
|
{
|
||||||
|
"Method": method.upper(),
|
||||||
|
"Endpoint": path,
|
||||||
|
"summary": details.get("summary", "요약 없음"),
|
||||||
|
}
|
||||||
|
)
|
||||||
|
df = pd.DataFrame(endpoint_data)
|
||||||
|
st.dataframe(df, use_container_width=True)
|
||||||
|
else:
|
||||||
|
st.warning("API 명세를 불러올 수 없습니다.")
|
||||||
|
|
||||||
|
# --- 메인 채팅 인터페이스 ---
|
||||||
|
st.markdown(
|
||||||
|
"<h1 style='text-align: center;'>💬 LLM-Gateway Guide Chatbot</h1>",
|
||||||
|
unsafe_allow_html=True,
|
||||||
|
)
|
||||||
|
st.caption(f"현재 사용 중인 API 명세: {API_SPEC_URL}")
|
||||||
|
|
||||||
|
|
||||||
|
# 대화 기록 초기화
|
||||||
|
if "messages" not in st.session_state:
|
||||||
|
st.session_state.messages = []
|
||||||
|
|
||||||
|
# 시작 메시지
|
||||||
|
if not st.session_state.messages:
|
||||||
|
st.info("안녕하세요! LLM-Gateway 에 대해 궁금한 점을 물어보세요.")
|
||||||
|
|
||||||
|
# 앱 재실행 시 기록에서 대화 메시지 표시
|
||||||
|
for message in st.session_state.messages:
|
||||||
|
avatar = "🧑💻" if message["role"] == "user" else "👻"
|
||||||
|
with st.chat_message(message["role"], avatar=avatar):
|
||||||
|
st.markdown(message["content"])
|
||||||
|
|
||||||
|
# 사용자 입력 수락
|
||||||
|
if prompt := st.chat_input("LLM-Gateway 에 대해 질문하세요..."):
|
||||||
|
if not api_spec:
|
||||||
|
st.error("질문을 처리할 수 없습니다: API 명세가 로드되지 않았습니다.")
|
||||||
|
else:
|
||||||
|
# 대화 기록에 사용자 메시지 추가
|
||||||
|
st.session_state.messages.append({"role": "user", "content": prompt})
|
||||||
|
# 채팅 메시지 컨테이너에 사용자 메시지 표시
|
||||||
|
with st.chat_message("user", avatar="🧑💻"):
|
||||||
|
st.markdown(prompt)
|
||||||
|
|
||||||
|
# 채팅 메시지 컨테이너에 어시스턴트 응답 표시
|
||||||
|
with st.chat_message("assistant", avatar="👻"):
|
||||||
|
message_placeholder = st.empty()
|
||||||
|
with st.spinner("답변을 생성하는 중..."):
|
||||||
|
try:
|
||||||
|
# Gemini를 위한 프롬프트 준비
|
||||||
|
full_prompt = f"""
|
||||||
|
당신은 다음 LLM-Gateway API 명세에 대한 전문가 어시스턴트입니다.
|
||||||
|
당신의 임무는 제공된 JSON 데이터를 기반으로만 질문에 답변하는 것입니다.
|
||||||
|
정보를 지어내지 마세요. 만약 명세에 답변이 없다면 없다고 말하세요.
|
||||||
|
모든 답변은 반드시 한국어로 제공해주세요.
|
||||||
|
|
||||||
|
**LLM-Gateway API 명세 (JSON):**
|
||||||
|
```json
|
||||||
|
{json.dumps(api_spec, indent=2, ensure_ascii=False)}
|
||||||
|
```
|
||||||
|
|
||||||
|
**사용자 질문:**
|
||||||
|
{prompt}
|
||||||
|
"""
|
||||||
|
response = model.generate_content(full_prompt)
|
||||||
|
|
||||||
|
if response.parts:
|
||||||
|
response_text = response.text
|
||||||
|
else:
|
||||||
|
# 응답이 차단될 수 있는 경우를 처리합니다
|
||||||
|
response_text = (
|
||||||
|
"죄송합니다, 해당 질문에 대한 답변을 드릴 수 없습니다."
|
||||||
|
)
|
||||||
|
|
||||||
|
message_placeholder.markdown(response_text)
|
||||||
|
st.session_state.messages.append(
|
||||||
|
{"role": "assistant", "content": response_text}
|
||||||
|
)
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
error_text = f"오류가 발생했습니다: {e}"
|
||||||
|
message_placeholder.markdown(error_text)
|
||||||
|
st.session_state.messages.append(
|
||||||
|
{"role": "assistant", "content": error_text}
|
||||||
|
)
|
||||||
46
pyproject.toml
Normal file
46
pyproject.toml
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
exclude = [
|
||||||
|
".bzr",
|
||||||
|
".direnv",
|
||||||
|
".eggs",
|
||||||
|
".git",
|
||||||
|
".git-rewrite",
|
||||||
|
".hg",
|
||||||
|
".mypy_cache",
|
||||||
|
".nox",
|
||||||
|
".pants.d",
|
||||||
|
".pytype",
|
||||||
|
".ruff_cache",
|
||||||
|
".svn",
|
||||||
|
".tox",
|
||||||
|
".venv",
|
||||||
|
"__pypackages__",
|
||||||
|
"_build",
|
||||||
|
"buck-out",
|
||||||
|
"build",
|
||||||
|
"dist",
|
||||||
|
"node_modules",
|
||||||
|
"venv",
|
||||||
|
]
|
||||||
|
|
||||||
|
line-length = 120
|
||||||
|
indent-width = 4
|
||||||
|
|
||||||
|
[lint]
|
||||||
|
# 기본적으로 Pyflakes('F')와 pycodestyle('E') 코드의 하위 집합을 활성화합니다.
|
||||||
|
select = ["E4", "E7", "E9", "F"]
|
||||||
|
ignore = []
|
||||||
|
|
||||||
|
# 활성화된 모든 규칙에 대한 수정 허용.
|
||||||
|
fixable = ["ALL"]
|
||||||
|
unfixable = []
|
||||||
|
|
||||||
|
# 밑줄 접두사가 붙은 경우 사용하지 않는 변수를 허용합니다.
|
||||||
|
dummy-variable-rgx = "^(_+|(_+[a-zA-Z0-9_]*[a-zA-Z0-9]+?))$"
|
||||||
|
|
||||||
|
[format]
|
||||||
|
quote-style = "double"
|
||||||
|
indent-style = "space"
|
||||||
|
skip-magic-trailing-comma = false
|
||||||
|
line-ending = "auto"
|
||||||
|
docstring-code-format = false
|
||||||
|
docstring-code-line-length = "dynamic"
|
||||||
5
requirements.txt
Normal file
5
requirements.txt
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
streamlit
|
||||||
|
google-generativeai
|
||||||
|
requests
|
||||||
|
pandas
|
||||||
|
pyarrow
|
||||||
1
static/api_spec.json
Normal file
1
static/api_spec.json
Normal file
File diff suppressed because one or more lines are too long
BIN
static/logo.png
Normal file
BIN
static/logo.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 17 KiB |
Reference in New Issue
Block a user