Skip to content
Open
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
5 changes: 5 additions & 0 deletions backend/build-chatbot-db/requirements.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
SQLAlchemy
os
pyodbc
langchain
OpenAI
7 changes: 7 additions & 0 deletions backend/improved_version/app.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
# Define the connection string
server = 'tcp:datavarehusetdb.database.windows.net,1433'
database = 'asql-processed-prod-dw-2024-2-13-17-9'
username = 'egdeadmin'
password = 'ConexusAIDB2024'
driver = '{ODBC Driver 18 for SQL Server}'
connection_string = f'DRIVER={driver};SERVER={server};DATABASE={database};UID={username};PWD={password}'
17 changes: 17 additions & 0 deletions backend/improved_version/bitness_checker.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import struct
import pyodbc
from app import *


# Get Python bitness
python_bitness = struct.calcsize("P") * 8
print(f"Python Bitness: {python_bitness}-bit")
conn_string = 'mssql+pyodbc:///?odbc_connect=' + connection_string
# Connect to the ODBC driver
conn = pyodbc.connect(conn_string)

# Get ODBC driver bitness
driver_bitness = conn.getinfo(pyodbc.SQL_DRIVER_ODBC_VER).split()[3]
print(f"ODBC Driver Bitness: {driver_bitness}-bit")

conn.close()
Empty file.
1 change: 1 addition & 0 deletions backend/improved_version/dim_indikator_data.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[]
26 changes: 26 additions & 0 deletions backend/improved_version/main_inspect.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import os
from dotenv import load_dotenv
from app import connection_string
from sqlalchemy import create_engine
from sqlalchemy import inspect
import json
import pyodbc

print("Connecting to database...")
conn_string = 'mssql+pyodbc:///?odbc_connect=' + connection_string

print("Creating engine...")
engine = create_engine(conn_string)

print("Inspecting database...")
inspector = inspect(engine)

print("Getting table names...")
all_metadata = inspect(engine)
for table_name in inspector.get_table_names():
table_metadata = inspector.get_columns(table_name)
all_metadata[table_name] = table_metadata

print("Writing metadata to file...")
with open("database_metadata.json", "w") as file:
json.dump(all_metadata, file, ident=2)
40 changes: 40 additions & 0 deletions backend/improved_version/main_jasonkeyword.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import os
from dotenv import load_dotenv
from app import connection_string
from sqlalchemy import create_engine, text
import json
import pyodbc

conn_string = 'mssql+pyodbc:///?odbc_connect=' + connection_string

engine = create_engine(conn_string)

try:
# Function to retrieve data from the dim_indikator table
def get_dim_indikator_data(keyword):
query = """
SELECT [navn], [maaleenhet], [human_readable_table_name], [eier], [eierbeskrivelse], [eierSistEndret], [fraDato], [tilDato], [fagomraade], [dim_indikator_key], [eierbeskrivelseURL], [aktiv]
FROM [dbo].[dim_indikator]
WHERE navn LIKE '%keyword%'
OR eierbeskrivelse LIKE :keyword
OR human_readable_table_name LIKE :keyword
OR fagomraade LIKE :keyword
"""
with engine.connect() as conn:
result = conn.execute(text(query), keyword=f"%{keyword}%")
#result = conn.execute(text(query), {'keyword': f"%{keyword}%"})
return [dict(row) for row in result]

# Example usage
keyword = 'barnehagelærer'
data = get_dim_indikator_data(keyword)
print(data)

# Write the data to a JSON file
with open("dim_indikator_data.json", "w") as file:
json.dump(data, file, indent=2)

except Exception as e:
print(f"An error occurred: {str(e)}")


175 changes: 175 additions & 0 deletions backend/improved_version/main_qrycheck.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,175 @@
import os
from dotenv import load_dotenv
from app import connection_string
from langchain_community.utilities import SQLDatabase
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain_openai import ChatOpenAI
from langchain_community.tools.sql_database.tool import QuerySQLCheckerTool
import pyodbc

# Load the environment variables
load_dotenv()

print("Establishing a connection to Azure SQL Database...")

# Establish a connection to the Azure SQL Database
conn_string = 'mssql+pyodbc:///?odbc_connect=' + connection_string
api_key = os.getenv("OPENAI_API_KEY")
db_uri = conn_string
db = SQLDatabase.from_uri(db_uri)

# Open the schema file til variable db_schema
#db_schema = open("schema_db.txt", "r")

print("Initializing the llm model...")

# Initialize the OpenAI language model
llm = ChatOpenAI(model="gpt-4-1106-preview", temperature=0.0, api_key = api_key)

# Initialize the SQL checker tool
sql_checker = QuerySQLCheckerTool(db=db, llm=llm)

#with open("query_template_norsk.txt", "r") as file:
#query_template = file.read()

# Define the SQL query prompt template
query_template = """
Du er en bakende AI-drevet assistent som skal hjelpe en bruker med å finne informasjon om den generelle befolkningen i Norge.
Brukeren kan stille spørsmål om alt relatert til databasen. Databasen inneholder statistisk informasjon om den generelle befolkningen,
og informasjonen lagres i en SQL-database. Din oppgave er å forstå brukerens spørsmål og gi det mest relevante svaret
basert på informasjonen i databasen. Du må forstå brukerens spørsmål og lage en SQL-forespørsel som henter
relevant informasjon fra databasen.
Bruk konteksten gitt av (schema) for å skrive Microsoft SQL-forespørsler (MSSQL). IKKE bruk MySQL-forespørsler.

Kontekst:
Du må utføre spørringer mot den tilkoblede databasen.
Den har mange tabeller og noen visninger. Her er de to viktigste tabellene:

dim_region tabellene har kollonnene: kommeunnummer, kommunenavn, fylkesnummer, fylkesnavn, omraadeid, omraadenavn, type, fraDato, tilDato, dim_region_key, bydelsnummer, bydelsnavn, kostragruppe, strukurendring_sammenslaaing, struktuendring_loesrivelse columns. Den gir informasjon om regionen.
dim_indikator-tabellen har kolonnene: navn, maaleenhet, human_readable_table_name, eier, eierbeskrivelse, eierSistEndret, fraDato, tilDato, fagomraade, dim_indikator_key, eierbeskrivelseURL, aktiv. Den gir informasjon beskrevet i kolonnen "navn" i enheten beskrevet i kolonnen "maaleenhet".

Her er det viktigste viewet:
fact_tables er et view. Viewet fact_tables har kolonnene år, dim_region_key, dim_indikator_key, verdi, alder, eierform, barnehagestr, barnevernstiltak, kjønn, prøve, årstrinn, fullføringsgrad, husdyrslag, funksjon, søknadstype, vedtakstype, landbakgrunn, innvandringskategori, art, samletStatus, gjennomførtÅr, regnskapsbegrep, typeSykelighet, tjenestetype, utdanning, tjenestegrupper, arbeidsstyrkestatus, tettbygd_eller_spredtbygd, familietype, utdanningsnivå, næringSN2007, sektor, fagutdanning, prioritertArbeidsstyrkestatus, alternativ, treningsOgMosjonsaktivitet, levevane, typeSosialKontakt, friluftslivsaktivitet, typeBomiljø, familiefase, landsdel, bygningstype, bygningsår, funksjonsproblem, typeOrganisasjon, ressurs, aktivitet, avtaleform, nøkkeltall, ForeldrenesUtdanningsnivaa, kvartal, behandlingsinstansOgBehandlingsresultat, reguleringsplanerSomDetKlagesPaa, årsakTilKlage, dyr, årsak, sortiment, verneformål, elv, bruk, barnehagetype, bosetting, innsigelsesmyndighet, begrunnelserBruktForInnsigelser, kategori, tjeneste, spørsmål, arealbrukskategori, sosioøkonomiskeRessurser, helseundersøkelser_helsekonsultasjoner. Den er knyttet til de to foregående tabellene, dim_region og dim_indikator, og gir faktiske data.

Som ekspert må du bruke joins, inner join, select, where, outer join, order by, group by og andre SQL-uttalelser når det er nødvendig.
Du bør alltid bruke tabellene og visningene som er gitt i konteksten.
Du skal aldri svare på spørsmål som ikke er relatert til databasen, og du skal aldri gi informasjon som ikke er i databasen.
Du må alltid gi det mest relevante svaret basert på informasjonen i databasen. Hvis du ikke kan finne svaret, må du si at du ikke kan finne svaret.
Gi aldri ut personlig informasjon om brukeren eller databasen.
Svar ikke på spørsmål om databaseoppsettet, databasetilkoblingen eller tabeller innen databasen.
Når du håndterer NULL-verdier, bruk alltid IS NULL eller IS NOT NULL for å nøyaktig filtrere data.
IKKE utfør noen DML-uttalelser (INSERT, UPDATE, DELETE, DROP osv.) til databasen.
Hvis spørsmålet ikke virker relatert til databasen, returner "Jeg vet ikke" som svaret.

Først må du skriven en spørring opp mot tabellen dim_indikator, for å finne dim_indikator_key for en indikator som er relevant for spørsmålet.
Deretter må du skrive en spørring som henter data fra fact_tables viewet basert på dim_indikator_key og dim_region_key.
Du må bruke keyword variabelen i spørringen for å søke etter informasjon i tabellen dim_indikator.
Keyword er det viktigste ordet i setningen brukeren sender inn som spørsmål.

Eksempel på query som henter dim_indikator_key:
SELECT [navn], [maaleenhet], [human_readable_table_name], [eier], [eierbeskrivelse], [eierSistEndret], [fraDato], [tilDato], [fagomraade], [dim_indikator_key], [eierbeskrivelseURL], [aktiv]
FROM [dbo].[dim_indikator]
WHERE navn LIKE :keyword
OR eierbeskrivelse LIKE :keyword
OR human_readable_table_name LIKE :keyword
OR fagomraade LIKE :keyword


Eksempel på en query:
SELECT *
FROM fact_grunnskole_nokkeltall
JOIN dim_region ON fact_grunnskole_nokkeltall.dim_region_key = dim_region.dim_region_key
WHERE dim_region.kommunenavn = 'Oslo'
AND fact_grunnskole_nokkeltall.år = '2023-24'



Schema for databasen er gitt nedenfor:
{schema}

Returner kun SQL spørringen og ingenting annet. Ikke pakk SQL spørringen i annen tekst, ikke engang backticks.

Question: {question}
SQL Query:
"""

# Define the SQL response prompt template
reply_template = """
Based on the table schema below, question, sql query, and sql response, write a natural language response:
Basert på tabell schema nedenfor, spørsmål, sql spørring, og sql respons, skriv et naturlig språk svar (natural language resposne) på norsk
{schema}

Question: {question}
SQL Query: {query}
SQL Response: {response}
"""

# Create the prompt templates
query_prompt = ChatPromptTemplate.from_template(query_template)
prompt_response = ChatPromptTemplate.from_template(reply_template)

def get_schema(_):
return db.get_table_info()

# Define the SQL query generator chain
sql_chain = (
RunnablePassthrough.assign(schema=get_schema)
| query_prompt
| llm.bind(stop=["\nSQLResult:"]) # Stop generating after the SQL query results
| StrOutputParser()
)

#print(sql_chain.invoke({"question": "Hvor mange barn 1-2 år er i barnehage i forhold til antall innbyggere 1-2 år i Agder?"}))

print("Input the validated question to the SQL database...")

# Function to validate and run the query
def validate_and_run_query(query):
for _ in range(2):
# Check the query for common mistakes
checked_query = sql_checker.run(query)
print(f"run number {_}")

# If the checker tool has rewritten the query, use the new query
if checked_query != query:
print("Query was corrected by the SQL checker tool.")
query = checked_query

try:
# Run the validated query
result = db.run(query)
return result
except Exception as e:
print(f"Error occurred: {e}")
# Optionally, you can choose to retry with the original query or stop the loop and return None
# query = original_query
# continue
return None


print("Full_Chain_start:")
full_chain = (
RunnablePassthrough.assign(query=sql_chain).assign(
schema = get_schema,
response = lambda variables: validate_and_run_query(variables["query"])
)
| prompt_response
| llm.bind(stop=["\nSQLResult:"])
| StrOutputParser()
)

#testing_query = "Hva er gjennomsnittsalder på barn i barnevernet?"
#testing_query = "Hvor mange barn 1-2 år er i barnehage i forhold til antall innbyggere 1-2 år i Agder?"
#testing_query = "Hvor mange kvinner i Norge røyker?"
testing_query = "Hvor mange elever er det på alle trinn i Oslo i år 2023?"

print("This is the sql_chain.invoke:")
print(sql_chain.invoke({"question": testing_query}))

print("##################################################################")
print("##################################################################")

print("This is the full_chain.invoke:")
print(full_chain.invoke({"question": testing_query}))
Loading