Spaces:
Sleeping
Sleeping
Commit
·
4970bec
1
Parent(s):
cf43659
fixed chatbot LLM
Browse files- .gitignore +7 -1
- app.py +396 -167
- requirements.txt +12 -6
- sklearn_wine.csv +179 -0
.gitignore
CHANGED
|
@@ -1,3 +1,9 @@
|
|
| 1 |
.streamlit/
|
| 2 |
app.ipynb
|
| 3 |
-
.ipynb_checkpoints/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
.streamlit/
|
| 2 |
app.ipynb
|
| 3 |
+
.ipynb_checkpoints/
|
| 4 |
+
app_local.py
|
| 5 |
+
.vscode/
|
| 6 |
+
ecommerce_duckdb.ipynb
|
| 7 |
+
openrouter_testings.ipynb
|
| 8 |
+
appBackup.py
|
| 9 |
+
prompt_instructions.txt
|
app.py
CHANGED
|
@@ -2,22 +2,277 @@ import streamlit as st
|
|
| 2 |
from tempfile import NamedTemporaryFile
|
| 3 |
|
| 4 |
import pprint
|
|
|
|
| 5 |
import os
|
| 6 |
-
from dotenv import load_dotenv, find_dotenv
|
| 7 |
-
import os
|
| 8 |
-
from langchain_openai import ChatOpenAI
|
| 9 |
from langchain_community.document_loaders import PyPDFLoader
|
| 10 |
from langchain.document_loaders.csv_loader import CSVLoader
|
| 11 |
from langchain.document_loaders import WebBaseLoader
|
|
|
|
| 12 |
|
| 13 |
import pandas as pd
|
| 14 |
import numpy as np
|
| 15 |
import pprint
|
|
|
|
|
|
|
| 16 |
|
| 17 |
defaultGoogleURL = "https://www.google.com/search?q=google+earnings"
|
|
|
|
|
|
|
|
|
|
|
|
|
| 18 |
OPEN_ROUTER_KEY = st.secrets["OPEN_ROUTER_KEY"]
|
| 19 |
-
|
| 20 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 21 |
|
| 22 |
def pretty_print_columns(text):
|
| 23 |
"""
|
|
@@ -31,172 +286,146 @@ def pretty_print_columns(text):
|
|
| 31 |
"""
|
| 32 |
return " ".join([line.strip() for line in text.splitlines() if line.strip()])
|
| 33 |
|
| 34 |
-
|
| 35 |
-
|
| 36 |
-
|
| 37 |
-
|
| 38 |
-
|
| 39 |
-
|
| 40 |
-
|
| 41 |
-
|
| 42 |
-
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
| 46 |
-
|
| 47 |
-
|
| 48 |
-
|
| 49 |
-
|
| 50 |
-
|
| 51 |
-
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
| 55 |
-
|
| 56 |
-
|
| 57 |
-
|
| 58 |
-
|
| 59 |
-
|
| 60 |
-
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
|
| 64 |
-
|
| 65 |
-
|
| 66 |
-
|
| 67 |
-
|
| 68 |
-
|
| 69 |
-
|
| 70 |
-
|
| 71 |
-
|
| 72 |
-
|
| 73 |
-
|
| 74 |
-
|
| 75 |
-
|
| 76 |
-
|
| 77 |
-
|
| 78 |
-
|
| 79 |
-
|
| 80 |
-
|
| 81 |
-
|
| 82 |
-
|
| 83 |
-
|
| 84 |
-
|
| 85 |
-
|
| 86 |
-
|
| 87 |
-
|
| 88 |
-
|
| 89 |
-
|
| 90 |
-
|
| 91 |
-
|
| 92 |
-
|
| 93 |
-
|
| 94 |
-
|
| 95 |
-
|
| 96 |
-
|
| 97 |
-
|
| 98 |
-
|
| 99 |
-
|
| 100 |
-
|
| 101 |
-
|
| 102 |
-
|
| 103 |
-
|
| 104 |
-
|
| 105 |
-
|
| 106 |
-
|
| 107 |
-
|
| 108 |
-
|
| 109 |
-
|
| 110 |
-
|
| 111 |
-
|
| 112 |
-
|
| 113 |
-
|
| 114 |
-
|
| 115 |
-
|
| 116 |
-
|
| 117 |
-
|
| 118 |
-
|
| 119 |
-
|
| 120 |
-
|
| 121 |
-
|
| 122 |
-
if genre==radioButtonList[1]: # Custom CSV Upload
|
| 123 |
-
loader = CSVLoader(file_path=uploadedFilename)
|
| 124 |
-
csv_data = loader.load()
|
| 125 |
-
elif genre==radioButtonList[2]: # Custom PDF Upload
|
| 126 |
-
loader = PyPDFLoader(uploadedFilename)
|
| 127 |
-
pdf_pages = loader.load_and_split()
|
| 128 |
-
|
| 129 |
-
enableChatBox = False
|
| 130 |
if genre==radioButtonList[1]: # Custom CSV Upload
|
| 131 |
-
|
|
|
|
|
|
|
|
|
|
| 132 |
elif genre==radioButtonList[2]: # Custom PDF Upload
|
| 133 |
-
|
| 134 |
-
|
| 135 |
-
|
| 136 |
-
|
| 137 |
-
|
| 138 |
-
|
| 139 |
-
|
| 140 |
-
|
| 141 |
-
|
| 142 |
-
|
| 143 |
-
|
| 144 |
-
if genre==radioButtonList[0]: # E-commerce CSV
|
| 145 |
-
# Initializing the agent
|
| 146 |
-
answer = llm.predict(f'''
|
| 147 |
-
Do not reply with a python code.
|
| 148 |
-
I have CSV file contents below:
|
| 149 |
-
|
| 150 |
-
{str(csv_data)}
|
| 151 |
-
|
| 152 |
-
{chatTextStr}
|
| 153 |
-
''')
|
| 154 |
-
st.write(answer)
|
| 155 |
-
|
| 156 |
-
elif genre==radioButtonList[1]: # Custom CSV Upload
|
| 157 |
-
# Initializing the agent
|
| 158 |
-
answer = llm.predict(f'''
|
| 159 |
-
Do not reply with a python code.
|
| 160 |
-
I have CSV file contents below:
|
| 161 |
-
|
| 162 |
-
{str(csv_data)}
|
| 163 |
-
|
| 164 |
-
{chatTextStr}
|
| 165 |
-
''')
|
| 166 |
-
st.write(answer)
|
| 167 |
-
|
| 168 |
-
elif genre==radioButtonList[2]: # Custom PDF Upload
|
| 169 |
-
pdf_answer = llm.predict(f'''
|
| 170 |
-
Do not reply with a python code.
|
| 171 |
-
I have PDF file contents below:
|
| 172 |
-
|
| 173 |
-
{str(pdf_pages)}
|
| 174 |
-
|
| 175 |
-
{chatTextStr}
|
| 176 |
-
''')
|
| 177 |
-
st.write(pdf_answer)
|
| 178 |
-
elif genre==radioButtonList[3]: # Google Alphabet URL Earnings Report
|
| 179 |
-
loader = WebBaseLoader(defaultGoogleURL)
|
| 180 |
-
web_data = loader.load()
|
| 181 |
-
answer = llm.predict(f'''
|
| 182 |
-
Do not reply with a python code.
|
| 183 |
-
I have website contents below:
|
| 184 |
|
| 185 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 186 |
|
| 187 |
-
|
| 188 |
-
|
|
|
|
|
|
|
|
|
|
| 189 |
|
| 190 |
-
|
| 191 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 192 |
loader = WebBaseLoader(urlInput)
|
| 193 |
web_data = loader.load()
|
| 194 |
-
|
| 195 |
-
|
| 196 |
-
I have website contents below:
|
| 197 |
-
|
| 198 |
-
{str(web_data)}
|
| 199 |
-
|
| 200 |
-
{chatTextStr}
|
| 201 |
-
''')
|
| 202 |
-
st.write(answer)
|
|
|
|
| 2 |
from tempfile import NamedTemporaryFile
|
| 3 |
|
| 4 |
import pprint
|
| 5 |
+
import re
|
| 6 |
import os
|
|
|
|
|
|
|
|
|
|
| 7 |
from langchain_community.document_loaders import PyPDFLoader
|
| 8 |
from langchain.document_loaders.csv_loader import CSVLoader
|
| 9 |
from langchain.document_loaders import WebBaseLoader
|
| 10 |
+
import duckdb
|
| 11 |
|
| 12 |
import pandas as pd
|
| 13 |
import numpy as np
|
| 14 |
import pprint
|
| 15 |
+
import requests
|
| 16 |
+
import json
|
| 17 |
|
| 18 |
defaultGoogleURL = "https://www.google.com/search?q=google+earnings"
|
| 19 |
+
OPEN_ROUTER_MODEL = "meta-llama/llama-3.3-70b-instruct:free"
|
| 20 |
+
DEFAULT_ECOMMERCE_CSV = "EcommerceDataset.csv"
|
| 21 |
+
|
| 22 |
+
# Input for OpenRouter API Key
|
| 23 |
OPEN_ROUTER_KEY = st.secrets["OPEN_ROUTER_KEY"]
|
| 24 |
+
|
| 25 |
+
if not OPEN_ROUTER_KEY:
|
| 26 |
+
st.warning("Please enter your OpenRouter API Key to proceed.")
|
| 27 |
+
st.stop()
|
| 28 |
+
|
| 29 |
+
def call_openrouter(content: str) -> str:
|
| 30 |
+
"""Send a chat request to OpenRouter and return a safe string response."""
|
| 31 |
+
try:
|
| 32 |
+
response = requests.post(
|
| 33 |
+
url="https://openrouter.ai/api/v1/chat/completions",
|
| 34 |
+
headers={
|
| 35 |
+
"Authorization": f"Bearer {OPEN_ROUTER_KEY}",
|
| 36 |
+
"Content-Type": "application/json"
|
| 37 |
+
},
|
| 38 |
+
data=json.dumps({
|
| 39 |
+
"model": OPEN_ROUTER_MODEL,
|
| 40 |
+
"messages": [
|
| 41 |
+
{
|
| 42 |
+
"role": "user",
|
| 43 |
+
"content": content
|
| 44 |
+
}
|
| 45 |
+
]
|
| 46 |
+
}),
|
| 47 |
+
timeout=60,
|
| 48 |
+
)
|
| 49 |
+
except Exception as exc:
|
| 50 |
+
return f"Request error: {exc}"
|
| 51 |
+
|
| 52 |
+
if not response.ok:
|
| 53 |
+
# Return status code plus body so the user knows what went wrong.
|
| 54 |
+
return f"Request failed ({response.status_code}): {response.text}"
|
| 55 |
+
|
| 56 |
+
try:
|
| 57 |
+
data = response.json()
|
| 58 |
+
except Exception as exc:
|
| 59 |
+
return f"Invalid JSON response: {exc} | body: {response.text}"
|
| 60 |
+
|
| 61 |
+
try:
|
| 62 |
+
return data["choices"][0]["message"]["content"]
|
| 63 |
+
except Exception:
|
| 64 |
+
return f"Unexpected response format: {data}"
|
| 65 |
+
|
| 66 |
+
def call_openrouter_messages(messages) -> str:
|
| 67 |
+
"""Generic OpenRouter call that accepts a messages list."""
|
| 68 |
+
try:
|
| 69 |
+
response = requests.post(
|
| 70 |
+
url="https://openrouter.ai/api/v1/chat/completions",
|
| 71 |
+
headers={
|
| 72 |
+
"Authorization": f"Bearer {OPEN_ROUTER_KEY}",
|
| 73 |
+
"Content-Type": "application/json"
|
| 74 |
+
},
|
| 75 |
+
data=json.dumps({
|
| 76 |
+
"model": OPEN_ROUTER_MODEL,
|
| 77 |
+
"messages": messages
|
| 78 |
+
}),
|
| 79 |
+
timeout=60,
|
| 80 |
+
)
|
| 81 |
+
except Exception as exc:
|
| 82 |
+
return f"Request error: {exc}"
|
| 83 |
+
|
| 84 |
+
if not response.ok:
|
| 85 |
+
return f"Request failed ({response.status_code}): {response.text}"
|
| 86 |
+
|
| 87 |
+
try:
|
| 88 |
+
data = response.json()
|
| 89 |
+
except Exception as exc:
|
| 90 |
+
return f"Invalid JSON response: {exc} | body: {response.text}"
|
| 91 |
+
|
| 92 |
+
try:
|
| 93 |
+
return data["choices"][0]["message"]["content"]
|
| 94 |
+
except Exception:
|
| 95 |
+
return f"Unexpected response format: {data}"
|
| 96 |
+
|
| 97 |
+
def ask_llm(question: str, schema_text: str) -> str:
|
| 98 |
+
"""Ask the model to generate a DuckDB SQL query for the given question and schema."""
|
| 99 |
+
messages = [
|
| 100 |
+
{
|
| 101 |
+
"role": "system",
|
| 102 |
+
"content": f"""
|
| 103 |
+
You are a data analyst.
|
| 104 |
+
|
| 105 |
+
You MUST use ONLY this table:
|
| 106 |
+
- Table name: data
|
| 107 |
+
|
| 108 |
+
Schema:
|
| 109 |
+
{schema_text}
|
| 110 |
+
|
| 111 |
+
Rules:
|
| 112 |
+
- Use ONLY table name "data"
|
| 113 |
+
- Return ONE valid DuckDB SQL query
|
| 114 |
+
- Do NOT explain
|
| 115 |
+
- Do NOT use markdown
|
| 116 |
+
"""
|
| 117 |
+
},
|
| 118 |
+
{"role": "user", "content": question},
|
| 119 |
+
]
|
| 120 |
+
return call_openrouter_messages(messages)
|
| 121 |
+
|
| 122 |
+
def explain_result(question: str, df: pd.DataFrame) -> str:
|
| 123 |
+
"""Ask the model to explain the result set in plain language."""
|
| 124 |
+
try:
|
| 125 |
+
result_text = df.to_string(index=False)
|
| 126 |
+
except Exception:
|
| 127 |
+
result_text = str(df)
|
| 128 |
+
|
| 129 |
+
messages = [
|
| 130 |
+
{
|
| 131 |
+
"role": "system",
|
| 132 |
+
"content": """
|
| 133 |
+
You are a data analyst.
|
| 134 |
+
|
| 135 |
+
Given a user's question and a query result,
|
| 136 |
+
produce a concise, human-like explanation.
|
| 137 |
+
|
| 138 |
+
Rules:
|
| 139 |
+
- Do NOT mention SQL, databases, or tables
|
| 140 |
+
- Do NOT explain how the data was computed
|
| 141 |
+
- Be clear and business-friendly
|
| 142 |
+
"""
|
| 143 |
+
},
|
| 144 |
+
{
|
| 145 |
+
"role": "user",
|
| 146 |
+
"content": f"""
|
| 147 |
+
Question:
|
| 148 |
+
{question}
|
| 149 |
+
|
| 150 |
+
Query Result:
|
| 151 |
+
{result_text}
|
| 152 |
+
"""
|
| 153 |
+
},
|
| 154 |
+
]
|
| 155 |
+
return call_openrouter_messages(messages)
|
| 156 |
+
|
| 157 |
+
def sanitize_dataframe(df: pd.DataFrame):
|
| 158 |
+
"""Return a copy of df with column names sanitized for SQL identifiers."""
|
| 159 |
+
if df is None or not isinstance(df, pd.DataFrame):
|
| 160 |
+
return df, {}
|
| 161 |
+
rename_map = {}
|
| 162 |
+
used = set()
|
| 163 |
+
for col in df.columns:
|
| 164 |
+
new_col = re.sub(r"[^0-9a-zA-Z_]+", "_", str(col))
|
| 165 |
+
new_col = new_col.strip("_")
|
| 166 |
+
if re.match(r"^[0-9]", new_col):
|
| 167 |
+
new_col = f"col_{new_col}"
|
| 168 |
+
if not new_col:
|
| 169 |
+
new_col = "col"
|
| 170 |
+
base = new_col
|
| 171 |
+
idx = 1
|
| 172 |
+
while new_col in used:
|
| 173 |
+
new_col = f"{base}_{idx}"
|
| 174 |
+
idx += 1
|
| 175 |
+
used.add(new_col)
|
| 176 |
+
rename_map[col] = new_col
|
| 177 |
+
return df.rename(columns=rename_map), rename_map
|
| 178 |
+
|
| 179 |
+
def run_duckdb_qa(question: str, dataframe: pd.DataFrame) -> str:
|
| 180 |
+
"""Generate SQL via LLM, run it on DuckDB, and explain the result."""
|
| 181 |
+
if not question.strip():
|
| 182 |
+
return "Please enter a question."
|
| 183 |
+
if dataframe is None or not isinstance(dataframe, pd.DataFrame):
|
| 184 |
+
return "No CSV data loaded."
|
| 185 |
+
clean_df, rename_map = sanitize_dataframe(dataframe)
|
| 186 |
+
con = duckdb.connect()
|
| 187 |
+
try:
|
| 188 |
+
con.register("data", clean_df)
|
| 189 |
+
schema_df = con.execute("DESCRIBE data").fetch_df()
|
| 190 |
+
schema_text = schema_df.to_string(index=False)
|
| 191 |
+
sql = ask_llm(question, schema_text)
|
| 192 |
+
if not isinstance(sql, str):
|
| 193 |
+
return f"Unexpected SQL response: {sql}"
|
| 194 |
+
sql = sql.strip().strip(";")
|
| 195 |
+
sql = re.sub(r"\bSTDEV\s*\(", "STDDEV(", sql, flags=re.IGNORECASE)
|
| 196 |
+
result_df = con.execute(sql).fetch_df()
|
| 197 |
+
except Exception as exc:
|
| 198 |
+
return f"SQL error: {exc}\nSQL used:\n{locals().get('sql', 'N/A')}"
|
| 199 |
+
finally:
|
| 200 |
+
con.close()
|
| 201 |
+
|
| 202 |
+
return explain_result(question, result_df)
|
| 203 |
+
|
| 204 |
+
def format_data_preview(data, max_chars: int = 12000) -> str:
|
| 205 |
+
"""Return a trimmed, human-friendly preview to keep prompts under token limits."""
|
| 206 |
+
if data is None:
|
| 207 |
+
return "No data loaded."
|
| 208 |
+
try:
|
| 209 |
+
if isinstance(data, pd.DataFrame):
|
| 210 |
+
preview = data.head(20).to_csv(index=False)
|
| 211 |
+
elif isinstance(data, list):
|
| 212 |
+
chunks = []
|
| 213 |
+
for doc in data[:5]:
|
| 214 |
+
text = getattr(doc, "page_content", str(doc))
|
| 215 |
+
if len(text) > 1500:
|
| 216 |
+
text = text[:1500] + "...[truncated]"
|
| 217 |
+
chunks.append(text)
|
| 218 |
+
preview = "\n\n".join(chunks)
|
| 219 |
+
else:
|
| 220 |
+
preview = str(data)
|
| 221 |
+
except Exception as exc:
|
| 222 |
+
preview = f"Could not format data preview: {exc}"
|
| 223 |
+
if len(preview) > max_chars:
|
| 224 |
+
preview = preview[:max_chars] + "...[truncated]"
|
| 225 |
+
return preview
|
| 226 |
+
|
| 227 |
+
def summarize_csv(dataframe: pd.DataFrame) -> str:
|
| 228 |
+
"""Build a compact summary (top items, payment mix) from a CSV DataFrame."""
|
| 229 |
+
if dataframe is None or not isinstance(dataframe, pd.DataFrame):
|
| 230 |
+
return ""
|
| 231 |
+
summary_lines = []
|
| 232 |
+
|
| 233 |
+
quantity_col = next((c for c in dataframe.columns if c.lower().startswith("quantity")), None)
|
| 234 |
+
desc_col = None
|
| 235 |
+
for candidate in ("Description", "Product", "Item", "Product_Name"):
|
| 236 |
+
if candidate in dataframe.columns:
|
| 237 |
+
desc_col = candidate
|
| 238 |
+
break
|
| 239 |
+
payment_col = next((c for c in dataframe.columns if "payment" in c.lower()), None)
|
| 240 |
+
|
| 241 |
+
if quantity_col and desc_col:
|
| 242 |
+
try:
|
| 243 |
+
top_items = (
|
| 244 |
+
dataframe.groupby(desc_col)[quantity_col]
|
| 245 |
+
.sum()
|
| 246 |
+
.sort_values(ascending=False)
|
| 247 |
+
.head(10)
|
| 248 |
+
)
|
| 249 |
+
summary_lines.append("Top items by quantity (sum):")
|
| 250 |
+
summary_lines.append(top_items.to_string())
|
| 251 |
+
except Exception as exc:
|
| 252 |
+
summary_lines.append(f"Could not compute top items: {exc}")
|
| 253 |
+
|
| 254 |
+
if payment_col:
|
| 255 |
+
try:
|
| 256 |
+
payment_counts = dataframe[payment_col].value_counts().head(10)
|
| 257 |
+
summary_lines.append("\nPayment method counts:")
|
| 258 |
+
summary_lines.append(payment_counts.to_string())
|
| 259 |
+
except Exception as exc:
|
| 260 |
+
summary_lines.append(f"Could not compute payment counts: {exc}")
|
| 261 |
+
|
| 262 |
+
return "\n".join(summary_lines)
|
| 263 |
+
|
| 264 |
+
def build_prompt(label: str, data, question: str, summary: str = "") -> str:
|
| 265 |
+
preview = format_data_preview(data)
|
| 266 |
+
summary_text = summary.strip()
|
| 267 |
+
summary_block = f"\nData summary:\n{summary_text}\n" if summary_text else ""
|
| 268 |
+
return f"""Do not reply with a python code.
|
| 269 |
+
Data preview ({label}, truncated to avoid context limits):
|
| 270 |
+
|
| 271 |
+
{preview}
|
| 272 |
+
|
| 273 |
+
{summary_block}
|
| 274 |
+
User question: {question}
|
| 275 |
+
"""
|
| 276 |
|
| 277 |
def pretty_print_columns(text):
|
| 278 |
"""
|
|
|
|
| 286 |
"""
|
| 287 |
return " ".join([line.strip() for line in text.splitlines() if line.strip()])
|
| 288 |
|
| 289 |
+
radioButtonList = ["E-commerce CSV (https://www.kaggle.com/datasets/mervemenekse/ecommerce-dataset)",
|
| 290 |
+
"Upload my own CSV",
|
| 291 |
+
"Upload my own PDF",
|
| 292 |
+
f"URL Chat with Google's Latest Earnings ({defaultGoogleURL})",
|
| 293 |
+
"Enter my own URL"]
|
| 294 |
+
|
| 295 |
+
# Add some designs to the radio buttons
|
| 296 |
+
st.markdown("""
|
| 297 |
+
<style>
|
| 298 |
+
.stRadio {
|
| 299 |
+
padding: 10px;
|
| 300 |
+
border-radius: 5px;
|
| 301 |
+
background-color: #f5f5f5;
|
| 302 |
+
}
|
| 303 |
+
|
| 304 |
+
.stRadio input[type="radio"] {
|
| 305 |
+
position: absolute;
|
| 306 |
+
opacity: 0;
|
| 307 |
+
cursor: pointer;
|
| 308 |
+
}
|
| 309 |
+
|
| 310 |
+
.stRadio label {
|
| 311 |
+
display: flex;
|
| 312 |
+
justify-content: center;
|
| 313 |
+
align-items: center;
|
| 314 |
+
cursor: pointer;
|
| 315 |
+
font-size: 16px;
|
| 316 |
+
color: #333;
|
| 317 |
+
}
|
| 318 |
+
|
| 319 |
+
.stRadio label:hover {
|
| 320 |
+
color: #000;
|
| 321 |
+
}
|
| 322 |
+
|
| 323 |
+
.stRadio.st-selected input[type="radio"] ~ label {
|
| 324 |
+
color: #000;
|
| 325 |
+
background-color: #d9d9d9;
|
| 326 |
+
}
|
| 327 |
+
</style>
|
| 328 |
+
""", unsafe_allow_html=True)
|
| 329 |
+
|
| 330 |
+
genre = st.radio(
|
| 331 |
+
"Tired of reading your files? Chat with it using AI! Choose dataset to finetune", radioButtonList, index=0
|
| 332 |
+
)
|
| 333 |
+
|
| 334 |
+
pdfCSVURLText = ""
|
| 335 |
+
exampleQuestion = ""
|
| 336 |
+
csv_data = None
|
| 337 |
+
pdf_pages = None
|
| 338 |
+
|
| 339 |
+
if genre==radioButtonList[1]:
|
| 340 |
+
pdfCSVURLText = "CSV"
|
| 341 |
+
exampleQuestion = "What are the data columns?"
|
| 342 |
+
elif genre==radioButtonList[2]:
|
| 343 |
+
pdfCSVURLText = "PDF"
|
| 344 |
+
exampleQuestion = "Can you summarize the contents?"
|
| 345 |
+
elif genre==radioButtonList[3]:
|
| 346 |
+
pdfCSVURLText = "URL"
|
| 347 |
+
exampleQuestion = "What is Google's latest earnings?"
|
| 348 |
+
elif genre==radioButtonList[4]:
|
| 349 |
+
pdfCSVURLText = "URL"
|
| 350 |
+
exampleQuestion = "Can you summarize the contents?"
|
| 351 |
+
else: # Default, E-commerce CSV
|
| 352 |
+
pdfCSVURLText = "CSV"
|
| 353 |
+
exampleQuestion = "Question1: What was the most sold item? Question2: What was the most common payment?"
|
| 354 |
+
if os.path.exists(DEFAULT_ECOMMERCE_CSV):
|
| 355 |
+
try:
|
| 356 |
+
csv_data = pd.read_csv(DEFAULT_ECOMMERCE_CSV)
|
| 357 |
+
except Exception as exc:
|
| 358 |
+
st.warning(f"Problem loading {DEFAULT_ECOMMERCE_CSV} ({exc}). Falling back to a small sample dataset.")
|
| 359 |
+
if csv_data is None:
|
| 360 |
+
# Keep a tiny inline sample so the app still works even when the CSV is missing locally.
|
| 361 |
+
csv_data = pd.DataFrame(
|
| 362 |
+
[
|
| 363 |
+
{"InvoiceNo": "536365", "StockCode": "85123A", "Description": "White hanging heart", "Quantity": 6, "UnitPrice": 2.55, "Country": "United Kingdom"},
|
| 364 |
+
{"InvoiceNo": "536366", "StockCode": "71053", "Description": "White metal lantern", "Quantity": 6, "UnitPrice": 3.39, "Country": "United Kingdom"},
|
| 365 |
+
{"InvoiceNo": "536367", "StockCode": "84406B", "Description": "Pink mini hanging heart", "Quantity": 8, "UnitPrice": 1.65, "Country": "United Kingdom"},
|
| 366 |
+
]
|
| 367 |
+
)
|
| 368 |
+
st.info(f"{DEFAULT_ECOMMERCE_CSV} not found. Using an inline sample instead. Upload your own CSV if you need the full dataset.")
|
| 369 |
+
|
| 370 |
+
isCustomURL = genre==radioButtonList[4]
|
| 371 |
+
urlInput = st.text_input('Enter your own URL', '', placeholder=f"Type your URL here (e.g. {defaultGoogleURL})", disabled=not isCustomURL)
|
| 372 |
+
|
| 373 |
+
isCustomUpload = genre==radioButtonList[1] or genre==radioButtonList[2]
|
| 374 |
+
uploaded_file = st.file_uploader(f"Upload your own {pdfCSVURLText} here", type=pdfCSVURLText.lower(), disabled=not isCustomUpload)
|
| 375 |
+
uploadedFilename = ""
|
| 376 |
+
if uploaded_file is not None:
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 377 |
if genre==radioButtonList[1]: # Custom CSV Upload
|
| 378 |
+
try:
|
| 379 |
+
csv_data = pd.read_csv(uploaded_file)
|
| 380 |
+
except Exception as exc:
|
| 381 |
+
st.error(f"Could not read uploaded CSV: {exc}")
|
| 382 |
elif genre==radioButtonList[2]: # Custom PDF Upload
|
| 383 |
+
with NamedTemporaryFile(dir='.', suffix=f'.{pdfCSVURLText.lower()}', delete=False) as f:
|
| 384 |
+
f.write(uploaded_file.getbuffer())
|
| 385 |
+
uploadedFilename = f.name
|
| 386 |
+
try:
|
| 387 |
+
loader = PyPDFLoader(uploadedFilename)
|
| 388 |
+
pdf_pages = loader.load_and_split()
|
| 389 |
+
except Exception as exc:
|
| 390 |
+
st.error(f"Could not read uploaded PDF: {exc}")
|
| 391 |
+
finally:
|
| 392 |
+
if uploadedFilename and os.path.exists(uploadedFilename):
|
| 393 |
+
os.remove(uploadedFilename)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 394 |
|
| 395 |
+
enableChatBox = False
|
| 396 |
+
if genre==radioButtonList[1]: # Custom CSV Upload
|
| 397 |
+
enableChatBox = isinstance(csv_data, pd.DataFrame)
|
| 398 |
+
elif genre==radioButtonList[2]: # Custom PDF Upload
|
| 399 |
+
enableChatBox = pdf_pages is not None
|
| 400 |
+
elif genre==radioButtonList[3]: # Google Alphabet URL Earnings Report
|
| 401 |
+
enableChatBox = True
|
| 402 |
+
elif genre==radioButtonList[4]: # Custom URL
|
| 403 |
+
enableChatBox = True
|
| 404 |
+
else: # E-commerce CSV
|
| 405 |
+
enableChatBox = True
|
| 406 |
|
| 407 |
+
chatTextStr = st.text_input(f'Ask me anything about this {pdfCSVURLText}', '', placeholder=f"Type here (e.g. {exampleQuestion})", disabled=not enableChatBox)
|
| 408 |
+
chatWithPDFButton = "CLICK HERE TO START CHATTING"
|
| 409 |
+
if st.button(chatWithPDFButton, disabled=not enableChatBox and not chatTextStr): # Button Cliked
|
| 410 |
+
if genre==radioButtonList[0]: # E-commerce CSV
|
| 411 |
+
st.write(run_duckdb_qa(chatTextStr, csv_data))
|
| 412 |
|
| 413 |
+
elif genre==radioButtonList[1]: # Custom CSV Upload
|
| 414 |
+
st.write(run_duckdb_qa(chatTextStr, csv_data))
|
| 415 |
+
|
| 416 |
+
elif genre==radioButtonList[2]: # Custom PDF Upload
|
| 417 |
+
content = build_prompt("Uploaded PDF", pdf_pages, chatTextStr)
|
| 418 |
+
st.write(call_openrouter(content))
|
| 419 |
+
elif genre==radioButtonList[3]: # Google Alphabet URL Earnings Report
|
| 420 |
+
loader = WebBaseLoader(defaultGoogleURL)
|
| 421 |
+
web_data = loader.load()
|
| 422 |
+
content = build_prompt("Google earnings URL", web_data, chatTextStr)
|
| 423 |
+
st.write(call_openrouter(content))
|
| 424 |
+
elif genre==radioButtonList[4]: # Custom URL
|
| 425 |
+
if not urlInput.strip():
|
| 426 |
+
st.warning("Please enter a URL first.")
|
| 427 |
+
else:
|
| 428 |
loader = WebBaseLoader(urlInput)
|
| 429 |
web_data = loader.load()
|
| 430 |
+
content = build_prompt("Custom URL", web_data, chatTextStr)
|
| 431 |
+
st.write(call_openrouter(content))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
requirements.txt
CHANGED
|
@@ -1,6 +1,12 @@
|
|
| 1 |
-
|
| 2 |
-
|
| 3 |
-
|
| 4 |
-
|
| 5 |
-
|
| 6 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
streamlit==1.52.1
|
| 2 |
+
pandas==2.3.3
|
| 3 |
+
numpy==1.26.4
|
| 4 |
+
requests==2.32.5
|
| 5 |
+
langchain==0.3.8
|
| 6 |
+
langchain_community==0.3.8
|
| 7 |
+
langchain_openai==0.2.9
|
| 8 |
+
beautifulsoup4==4.14.3
|
| 9 |
+
pypdf==6.4.1
|
| 10 |
+
duckdb==1.4.3
|
| 11 |
+
pdf2image==1.17.0
|
| 12 |
+
python-dotenv==1.2.1
|
sklearn_wine.csv
ADDED
|
@@ -0,0 +1,179 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
alcohol,malic_acid,ash,alcalinity_of_ash,magnesium,total_phenols,flavanoids,nonflavanoid_phenols,proanthocyanins,color_intensity,hue,od280/od315_of_diluted_wines,proline,target
|
| 2 |
+
14.23,1.71,2.43,15.6,127.0,2.8,3.06,0.28,2.29,5.64,1.04,3.92,1065.0,0
|
| 3 |
+
13.2,1.78,2.14,11.2,100.0,2.65,2.76,0.26,1.28,4.38,1.05,3.4,1050.0,0
|
| 4 |
+
13.16,2.36,2.67,18.6,101.0,2.8,3.24,0.3,2.81,5.68,1.03,3.17,1185.0,0
|
| 5 |
+
14.37,1.95,2.5,16.8,113.0,3.85,3.49,0.24,2.18,7.8,0.86,3.45,1480.0,0
|
| 6 |
+
13.24,2.59,2.87,21.0,118.0,2.8,2.69,0.39,1.82,4.32,1.04,2.93,735.0,0
|
| 7 |
+
14.2,1.76,2.45,15.2,112.0,3.27,3.39,0.34,1.97,6.75,1.05,2.85,1450.0,0
|
| 8 |
+
14.39,1.87,2.45,14.6,96.0,2.5,2.52,0.3,1.98,5.25,1.02,3.58,1290.0,0
|
| 9 |
+
14.06,2.15,2.61,17.6,121.0,2.6,2.51,0.31,1.25,5.05,1.06,3.58,1295.0,0
|
| 10 |
+
14.83,1.64,2.17,14.0,97.0,2.8,2.98,0.29,1.98,5.2,1.08,2.85,1045.0,0
|
| 11 |
+
13.86,1.35,2.27,16.0,98.0,2.98,3.15,0.22,1.85,7.22,1.01,3.55,1045.0,0
|
| 12 |
+
14.1,2.16,2.3,18.0,105.0,2.95,3.32,0.22,2.38,5.75,1.25,3.17,1510.0,0
|
| 13 |
+
14.12,1.48,2.32,16.8,95.0,2.2,2.43,0.26,1.57,5.0,1.17,2.82,1280.0,0
|
| 14 |
+
13.75,1.73,2.41,16.0,89.0,2.6,2.76,0.29,1.81,5.6,1.15,2.9,1320.0,0
|
| 15 |
+
14.75,1.73,2.39,11.4,91.0,3.1,3.69,0.43,2.81,5.4,1.25,2.73,1150.0,0
|
| 16 |
+
14.38,1.87,2.38,12.0,102.0,3.3,3.64,0.29,2.96,7.5,1.2,3.0,1547.0,0
|
| 17 |
+
13.63,1.81,2.7,17.2,112.0,2.85,2.91,0.3,1.46,7.3,1.28,2.88,1310.0,0
|
| 18 |
+
14.3,1.92,2.72,20.0,120.0,2.8,3.14,0.33,1.97,6.2,1.07,2.65,1280.0,0
|
| 19 |
+
13.83,1.57,2.62,20.0,115.0,2.95,3.4,0.4,1.72,6.6,1.13,2.57,1130.0,0
|
| 20 |
+
14.19,1.59,2.48,16.5,108.0,3.3,3.93,0.32,1.86,8.7,1.23,2.82,1680.0,0
|
| 21 |
+
13.64,3.1,2.56,15.2,116.0,2.7,3.03,0.17,1.66,5.1,0.96,3.36,845.0,0
|
| 22 |
+
14.06,1.63,2.28,16.0,126.0,3.0,3.17,0.24,2.1,5.65,1.09,3.71,780.0,0
|
| 23 |
+
12.93,3.8,2.65,18.6,102.0,2.41,2.41,0.25,1.98,4.5,1.03,3.52,770.0,0
|
| 24 |
+
13.71,1.86,2.36,16.6,101.0,2.61,2.88,0.27,1.69,3.8,1.11,4.0,1035.0,0
|
| 25 |
+
12.85,1.6,2.52,17.8,95.0,2.48,2.37,0.26,1.46,3.93,1.09,3.63,1015.0,0
|
| 26 |
+
13.5,1.81,2.61,20.0,96.0,2.53,2.61,0.28,1.66,3.52,1.12,3.82,845.0,0
|
| 27 |
+
13.05,2.05,3.22,25.0,124.0,2.63,2.68,0.47,1.92,3.58,1.13,3.2,830.0,0
|
| 28 |
+
13.39,1.77,2.62,16.1,93.0,2.85,2.94,0.34,1.45,4.8,0.92,3.22,1195.0,0
|
| 29 |
+
13.3,1.72,2.14,17.0,94.0,2.4,2.19,0.27,1.35,3.95,1.02,2.77,1285.0,0
|
| 30 |
+
13.87,1.9,2.8,19.4,107.0,2.95,2.97,0.37,1.76,4.5,1.25,3.4,915.0,0
|
| 31 |
+
14.02,1.68,2.21,16.0,96.0,2.65,2.33,0.26,1.98,4.7,1.04,3.59,1035.0,0
|
| 32 |
+
13.73,1.5,2.7,22.5,101.0,3.0,3.25,0.29,2.38,5.7,1.19,2.71,1285.0,0
|
| 33 |
+
13.58,1.66,2.36,19.1,106.0,2.86,3.19,0.22,1.95,6.9,1.09,2.88,1515.0,0
|
| 34 |
+
13.68,1.83,2.36,17.2,104.0,2.42,2.69,0.42,1.97,3.84,1.23,2.87,990.0,0
|
| 35 |
+
13.76,1.53,2.7,19.5,132.0,2.95,2.74,0.5,1.35,5.4,1.25,3.0,1235.0,0
|
| 36 |
+
13.51,1.8,2.65,19.0,110.0,2.35,2.53,0.29,1.54,4.2,1.1,2.87,1095.0,0
|
| 37 |
+
13.48,1.81,2.41,20.5,100.0,2.7,2.98,0.26,1.86,5.1,1.04,3.47,920.0,0
|
| 38 |
+
13.28,1.64,2.84,15.5,110.0,2.6,2.68,0.34,1.36,4.6,1.09,2.78,880.0,0
|
| 39 |
+
13.05,1.65,2.55,18.0,98.0,2.45,2.43,0.29,1.44,4.25,1.12,2.51,1105.0,0
|
| 40 |
+
13.07,1.5,2.1,15.5,98.0,2.4,2.64,0.28,1.37,3.7,1.18,2.69,1020.0,0
|
| 41 |
+
14.22,3.99,2.51,13.2,128.0,3.0,3.04,0.2,2.08,5.1,0.89,3.53,760.0,0
|
| 42 |
+
13.56,1.71,2.31,16.2,117.0,3.15,3.29,0.34,2.34,6.13,0.95,3.38,795.0,0
|
| 43 |
+
13.41,3.84,2.12,18.8,90.0,2.45,2.68,0.27,1.48,4.28,0.91,3.0,1035.0,0
|
| 44 |
+
13.88,1.89,2.59,15.0,101.0,3.25,3.56,0.17,1.7,5.43,0.88,3.56,1095.0,0
|
| 45 |
+
13.24,3.98,2.29,17.5,103.0,2.64,2.63,0.32,1.66,4.36,0.82,3.0,680.0,0
|
| 46 |
+
13.05,1.77,2.1,17.0,107.0,3.0,3.0,0.28,2.03,5.04,0.88,3.35,885.0,0
|
| 47 |
+
14.21,4.04,2.44,18.9,111.0,2.85,2.65,0.3,1.25,5.24,0.87,3.33,1080.0,0
|
| 48 |
+
14.38,3.59,2.28,16.0,102.0,3.25,3.17,0.27,2.19,4.9,1.04,3.44,1065.0,0
|
| 49 |
+
13.9,1.68,2.12,16.0,101.0,3.1,3.39,0.21,2.14,6.1,0.91,3.33,985.0,0
|
| 50 |
+
14.1,2.02,2.4,18.8,103.0,2.75,2.92,0.32,2.38,6.2,1.07,2.75,1060.0,0
|
| 51 |
+
13.94,1.73,2.27,17.4,108.0,2.88,3.54,0.32,2.08,8.9,1.12,3.1,1260.0,0
|
| 52 |
+
13.05,1.73,2.04,12.4,92.0,2.72,3.27,0.17,2.91,7.2,1.12,2.91,1150.0,0
|
| 53 |
+
13.83,1.65,2.6,17.2,94.0,2.45,2.99,0.22,2.29,5.6,1.24,3.37,1265.0,0
|
| 54 |
+
13.82,1.75,2.42,14.0,111.0,3.88,3.74,0.32,1.87,7.05,1.01,3.26,1190.0,0
|
| 55 |
+
13.77,1.9,2.68,17.1,115.0,3.0,2.79,0.39,1.68,6.3,1.13,2.93,1375.0,0
|
| 56 |
+
13.74,1.67,2.25,16.4,118.0,2.6,2.9,0.21,1.62,5.85,0.92,3.2,1060.0,0
|
| 57 |
+
13.56,1.73,2.46,20.5,116.0,2.96,2.78,0.2,2.45,6.25,0.98,3.03,1120.0,0
|
| 58 |
+
14.22,1.7,2.3,16.3,118.0,3.2,3.0,0.26,2.03,6.38,0.94,3.31,970.0,0
|
| 59 |
+
13.29,1.97,2.68,16.8,102.0,3.0,3.23,0.31,1.66,6.0,1.07,2.84,1270.0,0
|
| 60 |
+
13.72,1.43,2.5,16.7,108.0,3.4,3.67,0.19,2.04,6.8,0.89,2.87,1285.0,0
|
| 61 |
+
12.37,0.94,1.36,10.6,88.0,1.98,0.57,0.28,0.42,1.95,1.05,1.82,520.0,1
|
| 62 |
+
12.33,1.1,2.28,16.0,101.0,2.05,1.09,0.63,0.41,3.27,1.25,1.67,680.0,1
|
| 63 |
+
12.64,1.36,2.02,16.8,100.0,2.02,1.41,0.53,0.62,5.75,0.98,1.59,450.0,1
|
| 64 |
+
13.67,1.25,1.92,18.0,94.0,2.1,1.79,0.32,0.73,3.8,1.23,2.46,630.0,1
|
| 65 |
+
12.37,1.13,2.16,19.0,87.0,3.5,3.1,0.19,1.87,4.45,1.22,2.87,420.0,1
|
| 66 |
+
12.17,1.45,2.53,19.0,104.0,1.89,1.75,0.45,1.03,2.95,1.45,2.23,355.0,1
|
| 67 |
+
12.37,1.21,2.56,18.1,98.0,2.42,2.65,0.37,2.08,4.6,1.19,2.3,678.0,1
|
| 68 |
+
13.11,1.01,1.7,15.0,78.0,2.98,3.18,0.26,2.28,5.3,1.12,3.18,502.0,1
|
| 69 |
+
12.37,1.17,1.92,19.6,78.0,2.11,2.0,0.27,1.04,4.68,1.12,3.48,510.0,1
|
| 70 |
+
13.34,0.94,2.36,17.0,110.0,2.53,1.3,0.55,0.42,3.17,1.02,1.93,750.0,1
|
| 71 |
+
12.21,1.19,1.75,16.8,151.0,1.85,1.28,0.14,2.5,2.85,1.28,3.07,718.0,1
|
| 72 |
+
12.29,1.61,2.21,20.4,103.0,1.1,1.02,0.37,1.46,3.05,0.906,1.82,870.0,1
|
| 73 |
+
13.86,1.51,2.67,25.0,86.0,2.95,2.86,0.21,1.87,3.38,1.36,3.16,410.0,1
|
| 74 |
+
13.49,1.66,2.24,24.0,87.0,1.88,1.84,0.27,1.03,3.74,0.98,2.78,472.0,1
|
| 75 |
+
12.99,1.67,2.6,30.0,139.0,3.3,2.89,0.21,1.96,3.35,1.31,3.5,985.0,1
|
| 76 |
+
11.96,1.09,2.3,21.0,101.0,3.38,2.14,0.13,1.65,3.21,0.99,3.13,886.0,1
|
| 77 |
+
11.66,1.88,1.92,16.0,97.0,1.61,1.57,0.34,1.15,3.8,1.23,2.14,428.0,1
|
| 78 |
+
13.03,0.9,1.71,16.0,86.0,1.95,2.03,0.24,1.46,4.6,1.19,2.48,392.0,1
|
| 79 |
+
11.84,2.89,2.23,18.0,112.0,1.72,1.32,0.43,0.95,2.65,0.96,2.52,500.0,1
|
| 80 |
+
12.33,0.99,1.95,14.8,136.0,1.9,1.85,0.35,2.76,3.4,1.06,2.31,750.0,1
|
| 81 |
+
12.7,3.87,2.4,23.0,101.0,2.83,2.55,0.43,1.95,2.57,1.19,3.13,463.0,1
|
| 82 |
+
12.0,0.92,2.0,19.0,86.0,2.42,2.26,0.3,1.43,2.5,1.38,3.12,278.0,1
|
| 83 |
+
12.72,1.81,2.2,18.8,86.0,2.2,2.53,0.26,1.77,3.9,1.16,3.14,714.0,1
|
| 84 |
+
12.08,1.13,2.51,24.0,78.0,2.0,1.58,0.4,1.4,2.2,1.31,2.72,630.0,1
|
| 85 |
+
13.05,3.86,2.32,22.5,85.0,1.65,1.59,0.61,1.62,4.8,0.84,2.01,515.0,1
|
| 86 |
+
11.84,0.89,2.58,18.0,94.0,2.2,2.21,0.22,2.35,3.05,0.79,3.08,520.0,1
|
| 87 |
+
12.67,0.98,2.24,18.0,99.0,2.2,1.94,0.3,1.46,2.62,1.23,3.16,450.0,1
|
| 88 |
+
12.16,1.61,2.31,22.8,90.0,1.78,1.69,0.43,1.56,2.45,1.33,2.26,495.0,1
|
| 89 |
+
11.65,1.67,2.62,26.0,88.0,1.92,1.61,0.4,1.34,2.6,1.36,3.21,562.0,1
|
| 90 |
+
11.64,2.06,2.46,21.6,84.0,1.95,1.69,0.48,1.35,2.8,1.0,2.75,680.0,1
|
| 91 |
+
12.08,1.33,2.3,23.6,70.0,2.2,1.59,0.42,1.38,1.74,1.07,3.21,625.0,1
|
| 92 |
+
12.08,1.83,2.32,18.5,81.0,1.6,1.5,0.52,1.64,2.4,1.08,2.27,480.0,1
|
| 93 |
+
12.0,1.51,2.42,22.0,86.0,1.45,1.25,0.5,1.63,3.6,1.05,2.65,450.0,1
|
| 94 |
+
12.69,1.53,2.26,20.7,80.0,1.38,1.46,0.58,1.62,3.05,0.96,2.06,495.0,1
|
| 95 |
+
12.29,2.83,2.22,18.0,88.0,2.45,2.25,0.25,1.99,2.15,1.15,3.3,290.0,1
|
| 96 |
+
11.62,1.99,2.28,18.0,98.0,3.02,2.26,0.17,1.35,3.25,1.16,2.96,345.0,1
|
| 97 |
+
12.47,1.52,2.2,19.0,162.0,2.5,2.27,0.32,3.28,2.6,1.16,2.63,937.0,1
|
| 98 |
+
11.81,2.12,2.74,21.5,134.0,1.6,0.99,0.14,1.56,2.5,0.95,2.26,625.0,1
|
| 99 |
+
12.29,1.41,1.98,16.0,85.0,2.55,2.5,0.29,1.77,2.9,1.23,2.74,428.0,1
|
| 100 |
+
12.37,1.07,2.1,18.5,88.0,3.52,3.75,0.24,1.95,4.5,1.04,2.77,660.0,1
|
| 101 |
+
12.29,3.17,2.21,18.0,88.0,2.85,2.99,0.45,2.81,2.3,1.42,2.83,406.0,1
|
| 102 |
+
12.08,2.08,1.7,17.5,97.0,2.23,2.17,0.26,1.4,3.3,1.27,2.96,710.0,1
|
| 103 |
+
12.6,1.34,1.9,18.5,88.0,1.45,1.36,0.29,1.35,2.45,1.04,2.77,562.0,1
|
| 104 |
+
12.34,2.45,2.46,21.0,98.0,2.56,2.11,0.34,1.31,2.8,0.8,3.38,438.0,1
|
| 105 |
+
11.82,1.72,1.88,19.5,86.0,2.5,1.64,0.37,1.42,2.06,0.94,2.44,415.0,1
|
| 106 |
+
12.51,1.73,1.98,20.5,85.0,2.2,1.92,0.32,1.48,2.94,1.04,3.57,672.0,1
|
| 107 |
+
12.42,2.55,2.27,22.0,90.0,1.68,1.84,0.66,1.42,2.7,0.86,3.3,315.0,1
|
| 108 |
+
12.25,1.73,2.12,19.0,80.0,1.65,2.03,0.37,1.63,3.4,1.0,3.17,510.0,1
|
| 109 |
+
12.72,1.75,2.28,22.5,84.0,1.38,1.76,0.48,1.63,3.3,0.88,2.42,488.0,1
|
| 110 |
+
12.22,1.29,1.94,19.0,92.0,2.36,2.04,0.39,2.08,2.7,0.86,3.02,312.0,1
|
| 111 |
+
11.61,1.35,2.7,20.0,94.0,2.74,2.92,0.29,2.49,2.65,0.96,3.26,680.0,1
|
| 112 |
+
11.46,3.74,1.82,19.5,107.0,3.18,2.58,0.24,3.58,2.9,0.75,2.81,562.0,1
|
| 113 |
+
12.52,2.43,2.17,21.0,88.0,2.55,2.27,0.26,1.22,2.0,0.9,2.78,325.0,1
|
| 114 |
+
11.76,2.68,2.92,20.0,103.0,1.75,2.03,0.6,1.05,3.8,1.23,2.5,607.0,1
|
| 115 |
+
11.41,0.74,2.5,21.0,88.0,2.48,2.01,0.42,1.44,3.08,1.1,2.31,434.0,1
|
| 116 |
+
12.08,1.39,2.5,22.5,84.0,2.56,2.29,0.43,1.04,2.9,0.93,3.19,385.0,1
|
| 117 |
+
11.03,1.51,2.2,21.5,85.0,2.46,2.17,0.52,2.01,1.9,1.71,2.87,407.0,1
|
| 118 |
+
11.82,1.47,1.99,20.8,86.0,1.98,1.6,0.3,1.53,1.95,0.95,3.33,495.0,1
|
| 119 |
+
12.42,1.61,2.19,22.5,108.0,2.0,2.09,0.34,1.61,2.06,1.06,2.96,345.0,1
|
| 120 |
+
12.77,3.43,1.98,16.0,80.0,1.63,1.25,0.43,0.83,3.4,0.7,2.12,372.0,1
|
| 121 |
+
12.0,3.43,2.0,19.0,87.0,2.0,1.64,0.37,1.87,1.28,0.93,3.05,564.0,1
|
| 122 |
+
11.45,2.4,2.42,20.0,96.0,2.9,2.79,0.32,1.83,3.25,0.8,3.39,625.0,1
|
| 123 |
+
11.56,2.05,3.23,28.5,119.0,3.18,5.08,0.47,1.87,6.0,0.93,3.69,465.0,1
|
| 124 |
+
12.42,4.43,2.73,26.5,102.0,2.2,2.13,0.43,1.71,2.08,0.92,3.12,365.0,1
|
| 125 |
+
13.05,5.8,2.13,21.5,86.0,2.62,2.65,0.3,2.01,2.6,0.73,3.1,380.0,1
|
| 126 |
+
11.87,4.31,2.39,21.0,82.0,2.86,3.03,0.21,2.91,2.8,0.75,3.64,380.0,1
|
| 127 |
+
12.07,2.16,2.17,21.0,85.0,2.6,2.65,0.37,1.35,2.76,0.86,3.28,378.0,1
|
| 128 |
+
12.43,1.53,2.29,21.5,86.0,2.74,3.15,0.39,1.77,3.94,0.69,2.84,352.0,1
|
| 129 |
+
11.79,2.13,2.78,28.5,92.0,2.13,2.24,0.58,1.76,3.0,0.97,2.44,466.0,1
|
| 130 |
+
12.37,1.63,2.3,24.5,88.0,2.22,2.45,0.4,1.9,2.12,0.89,2.78,342.0,1
|
| 131 |
+
12.04,4.3,2.38,22.0,80.0,2.1,1.75,0.42,1.35,2.6,0.79,2.57,580.0,1
|
| 132 |
+
12.86,1.35,2.32,18.0,122.0,1.51,1.25,0.21,0.94,4.1,0.76,1.29,630.0,2
|
| 133 |
+
12.88,2.99,2.4,20.0,104.0,1.3,1.22,0.24,0.83,5.4,0.74,1.42,530.0,2
|
| 134 |
+
12.81,2.31,2.4,24.0,98.0,1.15,1.09,0.27,0.83,5.7,0.66,1.36,560.0,2
|
| 135 |
+
12.7,3.55,2.36,21.5,106.0,1.7,1.2,0.17,0.84,5.0,0.78,1.29,600.0,2
|
| 136 |
+
12.51,1.24,2.25,17.5,85.0,2.0,0.58,0.6,1.25,5.45,0.75,1.51,650.0,2
|
| 137 |
+
12.6,2.46,2.2,18.5,94.0,1.62,0.66,0.63,0.94,7.1,0.73,1.58,695.0,2
|
| 138 |
+
12.25,4.72,2.54,21.0,89.0,1.38,0.47,0.53,0.8,3.85,0.75,1.27,720.0,2
|
| 139 |
+
12.53,5.51,2.64,25.0,96.0,1.79,0.6,0.63,1.1,5.0,0.82,1.69,515.0,2
|
| 140 |
+
13.49,3.59,2.19,19.5,88.0,1.62,0.48,0.58,0.88,5.7,0.81,1.82,580.0,2
|
| 141 |
+
12.84,2.96,2.61,24.0,101.0,2.32,0.6,0.53,0.81,4.92,0.89,2.15,590.0,2
|
| 142 |
+
12.93,2.81,2.7,21.0,96.0,1.54,0.5,0.53,0.75,4.6,0.77,2.31,600.0,2
|
| 143 |
+
13.36,2.56,2.35,20.0,89.0,1.4,0.5,0.37,0.64,5.6,0.7,2.47,780.0,2
|
| 144 |
+
13.52,3.17,2.72,23.5,97.0,1.55,0.52,0.5,0.55,4.35,0.89,2.06,520.0,2
|
| 145 |
+
13.62,4.95,2.35,20.0,92.0,2.0,0.8,0.47,1.02,4.4,0.91,2.05,550.0,2
|
| 146 |
+
12.25,3.88,2.2,18.5,112.0,1.38,0.78,0.29,1.14,8.21,0.65,2.0,855.0,2
|
| 147 |
+
13.16,3.57,2.15,21.0,102.0,1.5,0.55,0.43,1.3,4.0,0.6,1.68,830.0,2
|
| 148 |
+
13.88,5.04,2.23,20.0,80.0,0.98,0.34,0.4,0.68,4.9,0.58,1.33,415.0,2
|
| 149 |
+
12.87,4.61,2.48,21.5,86.0,1.7,0.65,0.47,0.86,7.65,0.54,1.86,625.0,2
|
| 150 |
+
13.32,3.24,2.38,21.5,92.0,1.93,0.76,0.45,1.25,8.42,0.55,1.62,650.0,2
|
| 151 |
+
13.08,3.9,2.36,21.5,113.0,1.41,1.39,0.34,1.14,9.4,0.57,1.33,550.0,2
|
| 152 |
+
13.5,3.12,2.62,24.0,123.0,1.4,1.57,0.22,1.25,8.6,0.59,1.3,500.0,2
|
| 153 |
+
12.79,2.67,2.48,22.0,112.0,1.48,1.36,0.24,1.26,10.8,0.48,1.47,480.0,2
|
| 154 |
+
13.11,1.9,2.75,25.5,116.0,2.2,1.28,0.26,1.56,7.1,0.61,1.33,425.0,2
|
| 155 |
+
13.23,3.3,2.28,18.5,98.0,1.8,0.83,0.61,1.87,10.52,0.56,1.51,675.0,2
|
| 156 |
+
12.58,1.29,2.1,20.0,103.0,1.48,0.58,0.53,1.4,7.6,0.58,1.55,640.0,2
|
| 157 |
+
13.17,5.19,2.32,22.0,93.0,1.74,0.63,0.61,1.55,7.9,0.6,1.48,725.0,2
|
| 158 |
+
13.84,4.12,2.38,19.5,89.0,1.8,0.83,0.48,1.56,9.01,0.57,1.64,480.0,2
|
| 159 |
+
12.45,3.03,2.64,27.0,97.0,1.9,0.58,0.63,1.14,7.5,0.67,1.73,880.0,2
|
| 160 |
+
14.34,1.68,2.7,25.0,98.0,2.8,1.31,0.53,2.7,13.0,0.57,1.96,660.0,2
|
| 161 |
+
13.48,1.67,2.64,22.5,89.0,2.6,1.1,0.52,2.29,11.75,0.57,1.78,620.0,2
|
| 162 |
+
12.36,3.83,2.38,21.0,88.0,2.3,0.92,0.5,1.04,7.65,0.56,1.58,520.0,2
|
| 163 |
+
13.69,3.26,2.54,20.0,107.0,1.83,0.56,0.5,0.8,5.88,0.96,1.82,680.0,2
|
| 164 |
+
12.85,3.27,2.58,22.0,106.0,1.65,0.6,0.6,0.96,5.58,0.87,2.11,570.0,2
|
| 165 |
+
12.96,3.45,2.35,18.5,106.0,1.39,0.7,0.4,0.94,5.28,0.68,1.75,675.0,2
|
| 166 |
+
13.78,2.76,2.3,22.0,90.0,1.35,0.68,0.41,1.03,9.58,0.7,1.68,615.0,2
|
| 167 |
+
13.73,4.36,2.26,22.5,88.0,1.28,0.47,0.52,1.15,6.62,0.78,1.75,520.0,2
|
| 168 |
+
13.45,3.7,2.6,23.0,111.0,1.7,0.92,0.43,1.46,10.68,0.85,1.56,695.0,2
|
| 169 |
+
12.82,3.37,2.3,19.5,88.0,1.48,0.66,0.4,0.97,10.26,0.72,1.75,685.0,2
|
| 170 |
+
13.58,2.58,2.69,24.5,105.0,1.55,0.84,0.39,1.54,8.66,0.74,1.8,750.0,2
|
| 171 |
+
13.4,4.6,2.86,25.0,112.0,1.98,0.96,0.27,1.11,8.5,0.67,1.92,630.0,2
|
| 172 |
+
12.2,3.03,2.32,19.0,96.0,1.25,0.49,0.4,0.73,5.5,0.66,1.83,510.0,2
|
| 173 |
+
12.77,2.39,2.28,19.5,86.0,1.39,0.51,0.48,0.64,9.899999,0.57,1.63,470.0,2
|
| 174 |
+
14.16,2.51,2.48,20.0,91.0,1.68,0.7,0.44,1.24,9.7,0.62,1.71,660.0,2
|
| 175 |
+
13.71,5.65,2.45,20.5,95.0,1.68,0.61,0.52,1.06,7.7,0.64,1.74,740.0,2
|
| 176 |
+
13.4,3.91,2.48,23.0,102.0,1.8,0.75,0.43,1.41,7.3,0.7,1.56,750.0,2
|
| 177 |
+
13.27,4.28,2.26,20.0,120.0,1.59,0.69,0.43,1.35,10.2,0.59,1.56,835.0,2
|
| 178 |
+
13.17,2.59,2.37,20.0,120.0,1.65,0.68,0.53,1.46,9.3,0.6,1.62,840.0,2
|
| 179 |
+
14.13,4.1,2.74,24.5,96.0,2.05,0.76,0.56,1.35,9.2,0.61,1.6,560.0,2
|