From 02c8bb816f8b85cb2c984251a0d7f2b54113397e Mon Sep 17 00:00:00 2001 From: bolade Date: Tue, 28 Oct 2025 21:09:47 +0100 Subject: [PATCH] made querying async --- app/__pycache__/main.cpython-312.pyc | Bin 6643 -> 6674 bytes app/db/__pycache__/db.cpython-312.pyc | Bin 1749 -> 1712 bytes app/db/db.py | 1 - app/main.py | 8 ++++---- .../__pycache__/querying.cpython-312.pyc | Bin 10699 -> 13636 bytes app/services/company_querying.py | 16 ++++++++++++---- app/services/querying.py | 16 +++++++++++----- 7 files changed, 27 insertions(+), 14 deletions(-) diff --git a/app/__pycache__/main.cpython-312.pyc b/app/__pycache__/main.cpython-312.pyc index 9932cb90f90797ea03be172b366a12a69d081d4e..4c7ce62d4f601a929a78c79e87ec2fa4c9d00b0d 100644 GIT binary patch delta 1031 zcma)4Jxmlq6rQ(xxA(i;&+aX|2M5QGzzP~pNGJpnqsd7~@VD_LTFpSj8AGrj8WS$$ zOe}103oMK_79@w&cE(;4D?$OCttAN*#>&~f0B1rjzGCKk-#71l^CojN@pe-Gq3cN^ z<6A#GUfI;0^=*}qEkX$ePO!NHDl%8NDk2yWQH%-=H3&47$3z_CA&&5bNMcf?FeTEM z7CP!7ALSh)gP9O(JS%dT6M4)F0}WxKDGFE+7FwYmp&sl3~9>s)VNNgz7BQkmCk{10maC zN;=a*53>H{dG98;>q8RVY94M~X`UKOz($zsNP5RSX(%mIw9tU&S(&BJvPFejEJ{0> zPHlE^QKL>hdV0Ol^L@r?krkN}n9!~z*y%noq9ryU8}`&Ar!B^U7G7B?TrV>F+=@Y2 zQEsWYBr6`aj@87>Tz=%;R!h$LYObwXXshPiszzJYYO0;%wC~9XA*)6C6@gcak zO#Lq^SHSj%wKaet?_+EQ2K;RN8bHOno0x;k{`V$lVU*va<9G6M1cQ0G6%^jGfeRFiz9Nn(&k4^86P4AgA`yDg2 z>Om@BTR2onX7iI`*{OdFhxW3=|7YmA)>Um{ r^@g!At&o@qbOtvDp#xyM9qhnD}sehz;DR3OMD delta 1007 zcmaJSs|2Aa0oUdAo#h2k{@LZh$uO2JcvQApu$5Kf<~?Egn0xbJ`OnT zJc?1SqRKVYcno7c-{!=50uw$CIvrd`of~Ly6HT7Pr6jk|;x^iTf#Rfi8q++38J@+g z&xM>$p2M7v!%i2^W8TLRrWea?BBW?7bL4567StYr@e(N0znhPis+$l_eX z!i`JjV3ws%?I?$gV-#Dc%cpBg#HlSJ1>JYO5%(yUFbu&xz8S2v3z>&1}` zZL~JF)!tE?_`4qv^)E~PuBVqz^PMnO!2M-j8T!w+xDh|o+Sk38ysTfGzaiYW6=$|u zoqkf?mSz{cPsvwc&k4HmoECP)y(a^}4hioN&8^sXRU-tE59I?Q!k*!Mw4cI1z=z1E diff --git a/app/db/__pycache__/db.cpython-312.pyc b/app/db/__pycache__/db.cpython-312.pyc index f367c0da854209e96ed71e99c2661405caf4345d..3a9580d69686fd388d7ff73921a8b322f471aa50 100644 GIT binary patch delta 610 zcmZ8eJ#Q015WPLG&-dXy+X*3Dupo*e5(_Cv&>@t9hAJs3uCXrOaX4h3&F+D8N|Asf zQBXzq7eEwFfkXlSfhHhDQ7#>Vf)0s}ikUNC%1ZmR^XARlo!MjYMu-nWxCXA%B^JLs z5CQvquySvA=!G6LOatr*C2@syjEsj1)98PcHEB=GDHHmrvcQ+lq5GF>Am>R(I?y=u zCY9gHrmXK9VGB%fG0&kjVbe_C3|8!`F54P zV%1qZ2KguRD--$q%FWv|7#AW=;MG_sxPQ>}(9oNIv3|sZvL?U5 zZ{_vsDqJChb5=fC$*n(bY6&%Kq+2AQD?1kpbEFF^&Bk%Et!Q9Htsm($8pn!LZI{5{G*Mq_U delta 590 zcmYjOy>HV%6u)!MPU852)0nnt5Trt=q*D2u5LJa(*nrtfDE}3to4s9Es z>AOB~wSv$_m>%(WW!}?L>v)*;^f=p59q`uS`-x+FN8 zZ~iKC%?=)11o0{ws|ff;ce${Jx+ssJ?j+t;IFG`~$D=sgRSZeFAL%?A#mYwJ>b)dN zb_OyXE5{t!oy{`J_5bpfp-NQ+L{&vtMB9NLWVWxcV1?sU(@q2P(Qe+sAxwKxSl)ci lKz~;@)1i2r$St)26#t1n0{C8{l)fj`4`kU~cMcf#@&{X2eAxg1 diff --git a/app/db/db.py b/app/db/db.py index ca7870b..e904339 100644 --- a/app/db/db.py +++ b/app/db/db.py @@ -1,5 +1,4 @@ import os -from pathlib import Path from typing import Annotated from fastapi import Depends diff --git a/app/main.py b/app/main.py index a017939..7a58476 100644 --- a/app/main.py +++ b/app/main.py @@ -1,6 +1,4 @@ import io -import logging -import os import pandas as pd from db.db import Base, db_dependency, engine @@ -44,6 +42,7 @@ class QueryRequest(BaseModel): } } + class CompanyQueryRequest(BaseModel): question: str @@ -54,6 +53,7 @@ class CompanyQueryRequest(BaseModel): } } + @app.get("/") def health(): return {"Hello": "World"} @@ -122,7 +122,7 @@ async def query_investors(request: QueryRequest): - "Healthcare investors in Europe" """ processor = QueryProcessor() - results = processor.process_query(request.question) + results = await processor.process_query(request.question) return results @@ -143,7 +143,7 @@ async def query_companies(request: CompanyQueryRequest): - "European startups founded after 2019" """ processor = CompanyQueryProcessor() - results = processor.process_query(request.question) + results = await processor.process_query(request.question) return results diff --git a/app/services/__pycache__/querying.cpython-312.pyc b/app/services/__pycache__/querying.cpython-312.pyc index 4221e662559258f8190bc902513e9d222249b2ca..056a5e3a3979f76695b1b6c2252de59bbdad9af6 100644 GIT binary patch delta 3694 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 diff --git a/app/services/company_querying.py b/app/services/company_querying.py index e002af3..aabc49c 100644 --- a/app/services/company_querying.py +++ b/app/services/company_querying.py @@ -1,3 +1,4 @@ +import asyncio import hashlib import logging import os @@ -116,11 +117,18 @@ Return ONLY the SQL query, no explanations or markdown.""", """Generate cache key from normalized question.""" return hashlib.md5(question.lower().strip().encode()).hexdigest() - def process_query(self, question: str) -> PaginatedResponse[CompanyData]: - """Process a query by generating and executing SQL directly. + # synchronous helper is provided below as `_process_query_sync` and an + # async wrapper `process_query` runs it in a thread. This keeps the + # FastAPI event loop non-blocking while reusing the existing sync code. + async def process_query(self, question: str) -> PaginatedResponse[CompanyData]: + """Async wrapper for process_query. Runs blocking work in a thread to avoid + blocking the event loop. + """ + return await asyncio.to_thread(self._process_query_sync, question) - Args: - question: The natural language query to process + def _process_query_sync(self, question: str) -> PaginatedResponse[CompanyData]: + """Synchronous implementation of process_query. This is run in a thread by + the async wrapper above. """ cache_key = self._get_cache_key(question) diff --git a/app/services/querying.py b/app/services/querying.py index 2a566eb..2a8bca5 100644 --- a/app/services/querying.py +++ b/app/services/querying.py @@ -1,3 +1,4 @@ +import asyncio import hashlib import logging import os @@ -126,14 +127,19 @@ Return ONLY the SQL query, no explanations or markdown.""", """Generate cache key from normalized question.""" return hashlib.md5(question.lower().strip().encode()).hexdigest() - def process_query( + async def process_query( self, question: str, project_id: Optional[int] = None ) -> PaginatedResponse[InvestmentResponse]: - """Process a query by generating and executing SQL directly. + """Async wrapper for process_query. Runs blocking work in a thread to avoid + blocking the event loop. + """ + return await asyncio.to_thread(self._process_query_sync, question, project_id) - Args: - question: The natural language query to process - project_id: Optional project ID for compatibility scoring + def _process_query_sync( + self, question: str, project_id: Optional[int] = None + ) -> PaginatedResponse[InvestmentResponse]: + """Synchronous implementation of process_query. This is run in a thread by + the async wrapper above. """ cache_key = self._get_cache_key(question)