├── .env.example ├── .gitignore ├── README.md ├── geogpt ├── __init__.py ├── main.py └── test_main.py ├── poetry.lock └── pyproject.toml /.env.example: -------------------------------------------------------------------------------- 1 | OPENAI_API_KEY=your_api_key_here 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Idea 2 | .idea/ 3 | 4 | # macOS 5 | .DS_Store 6 | 7 | # Byte-compiled / optimized files 8 | __pycache__/ 9 | *.py[cod] 10 | 11 | # Distribution / packaging 12 | build/ 13 | dist/ 14 | *.egg-info/ 15 | 16 | # Environment variables and secrets 17 | /.env 18 | 19 | # Sqlite3 geonames database 20 | geonames.sqlite3 -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # GeoGPT 2 | 3 | GeoGPT is an application that provides the latitude, longitude, and timezone for any address in the world for cities with more than 500 residents. 4 | It leverages OpenAI's GPT models to interpret and standardize addresses. 5 | Data from geonames.org is then utilized to furnish latitude, longitude, and timezone details. 6 | 7 | ## Setup & Installation 8 | 9 | 1. **Dependencies** 10 | 11 | Install the required dependencies using `poetry`: 12 | ```bash 13 | poetry install 14 | ``` 15 | 16 | 2. **API Key** 17 | 18 | Create a `.env` file and set your OpenAI API key within it: 19 | ``` env 20 | OPENAI_API_KEY=your_key_here 21 | ``` 22 | 23 | 3. **Create geonames.sqlite3 SQLite3 database** 24 | 25 | Fetch the `cities500.txt` file from Geoname at [this link](http://download.geonames.org/export/dump/), which provides details of all cities with a population greater than 500. 26 | 27 | 1. Open the SQLite shell: 28 | ``` bash 29 | sqlite3 geonames.sqlite3 30 | ``` 31 | 2. Create the `geoname` SQL table: 32 | ``` sql 33 | CREATE TABLE geoname ( 34 | geonameid INTEGER PRIMARY KEY, 35 | name TEXT, 36 | asciiname TEXT, 37 | alternatenames TEXT, 38 | latitude REAL, 39 | longitude REAL, 40 | feature_class TEXT, 41 | feature_code TEXT, 42 | country_code TEXT, 43 | cc2 TEXT, 44 | admin1_code TEXT, 45 | admin2_code TEXT, 46 | admin3_code TEXT, 47 | admin4_code TEXT, 48 | population INTEGER, 49 | elevation INTEGER, 50 | dem INTEGER, 51 | timezone TEXT, 52 | modification_date TEXT 53 | ); 54 | ``` 55 | 3. Set the field separator: 56 | ``` SQL 57 | .separator "\t" 58 | ``` 59 | 4. Import the `cities500.txt` file into the `geoname` table: 60 | ``` SQL 61 | .import cities500.txt geoname 62 | ``` 63 | 64 | 4. **Running the Application** 65 | 66 | Execute the FastAPI application using `uvicorn`: 67 | ```bash 68 | poetry run uvicorn geogpt.main:app --reload 69 | ``` 70 | 71 | ## Usage 72 | 73 | Submit an address to the endpoint, and the application will return details about the location, including its normalized name, country, longitude, latitude, and timezone. 74 | 75 | ## Contributing 76 | 77 | Contributions are welcome! Please fork the repository and open a pull request with your changes. Alternatively, open an issue to discuss suggestions or report bugs. 78 | -------------------------------------------------------------------------------- /geogpt/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sorieux/geoGPT/aacc9704640a155e0dff0eeba0822f5cd35c6c78/geogpt/__init__.py -------------------------------------------------------------------------------- /geogpt/main.py: -------------------------------------------------------------------------------- 1 | import json 2 | import logging 3 | import sqlite3 4 | 5 | import openai 6 | from decouple import config 7 | from fastapi import FastAPI, HTTPException, Body 8 | 9 | app = FastAPI() 10 | logging.basicConfig( 11 | level=logging.DEBUG, format="%(asctime)s - %(levelname)s - %(message)s" 12 | ) 13 | 14 | openai.api_key = config("OPENAI_API_KEY") 15 | 16 | 17 | def get_geoname(city: str, country_code: str): 18 | logging.info(f"Fetching geoname for city: {city} and country code: {country_code}") 19 | 20 | with sqlite3.connect("geonames.sqlite3") as con: 21 | cur = con.cursor() 22 | 23 | res = cur.execute( 24 | "SELECT name, country_code, longitude, latitude, timezone FROM geoname WHERE LOWER(asciiname)=? AND LOWER(country_code)=?", 25 | (city.lower(), country_code.lower()), 26 | ) 27 | 28 | data = res.fetchone() 29 | 30 | if not data: 31 | logging.warning( 32 | f"No data found in the database for city: {city} and country code: {country_code}" 33 | ) 34 | raise HTTPException(status_code=404, detail="Data not found in the database") 35 | 36 | name, country, longitude, latitude, timezone = data 37 | 38 | return { 39 | "name": name, 40 | "country": country, 41 | "longitude": longitude, 42 | "latitude": latitude, 43 | "timezone": timezone, 44 | } 45 | 46 | 47 | def get_city_country(address: str): 48 | messages = [ 49 | { 50 | "role": "system", 51 | "content": "Determine the city and country code from a possibly misspelled address. Return in JSON format: city and country_code", 52 | }, 53 | {"role": "user", "content": address}, 54 | ] 55 | 56 | response = openai.ChatCompletion.create( 57 | model="gpt-4", 58 | messages=messages, 59 | ) 60 | 61 | logging.debug(f"OpenAI API response: {response}") 62 | 63 | try: 64 | response_data = json.loads(response["choices"][0]["message"]["content"]) 65 | 66 | city = response_data["city"] 67 | country_code = response_data["country_code"] 68 | except (KeyError, json.JSONDecodeError): 69 | logging.error(f"Failed to parse OpenAI response") 70 | raise HTTPException(status_code=500, detail="Failed to parse OpenAI response") 71 | 72 | return city, country_code 73 | 74 | 75 | @app.get("/{address}") 76 | def main(address: str): 77 | city, country_code = get_city_country(address) 78 | res = get_geoname(city, country_code) 79 | return res 80 | -------------------------------------------------------------------------------- /geogpt/test_main.py: -------------------------------------------------------------------------------- 1 | from geogpt.main import app 2 | from fastapi.testclient import TestClient 3 | 4 | client = TestClient(app) 5 | 6 | expected_result = { 7 | "name": "Saint-Remy-en-Bouzemont-Saint-Genest-et-Isson", 8 | "country": "FR", 9 | "longitude": 4.65, 10 | "latitude": 48.63333, 11 | "timezone": "Europe/Paris", 12 | } 13 | 14 | def test_get_geoname_from_address(): 15 | address = "6 Rte de Drosnay, 51290 Saint-Remy-en-Bouzemont-Saint-Genest-et-Isson" 16 | response = client.get(f"/{address}") 17 | 18 | assert response.status_code == 200 19 | 20 | assert response.json() == expected_result 21 | 22 | def test_get_geoname_from_address_alterate(): 23 | address = "6 Rte de Drosay, 51290 Saint-Rmy-en-Bouzmont-Saint-Genet-et-Ison" 24 | response = client.get(f"/{address}") 25 | 26 | assert response.status_code == 200 27 | 28 | assert response.json() == expected_result 29 | -------------------------------------------------------------------------------- /poetry.lock: -------------------------------------------------------------------------------- 1 | [[package]] 2 | category = "main" 3 | description = "Async http client/server framework (asyncio)" 4 | name = "aiohttp" 5 | optional = false 6 | python-versions = ">=3.6" 7 | version = "3.8.5" 8 | 9 | [package.dependencies] 10 | aiosignal = ">=1.1.2" 11 | async-timeout = ">=4.0.0a3,<5.0" 12 | attrs = ">=17.3.0" 13 | charset-normalizer = ">=2.0,<4.0" 14 | frozenlist = ">=1.1.1" 15 | multidict = ">=4.5,<7.0" 16 | yarl = ">=1.0,<2.0" 17 | 18 | [package.extras] 19 | speedups = ["aiodns", "brotli", "cchardet"] 20 | 21 | [[package]] 22 | category = "main" 23 | description = "aiosignal: a list of registered asynchronous callbacks" 24 | name = "aiosignal" 25 | optional = false 26 | python-versions = ">=3.7" 27 | version = "1.3.1" 28 | 29 | [package.dependencies] 30 | frozenlist = ">=1.1.0" 31 | 32 | [[package]] 33 | category = "main" 34 | description = "Reusable constraint types to use with typing.Annotated" 35 | name = "annotated-types" 36 | optional = false 37 | python-versions = ">=3.7" 38 | version = "0.5.0" 39 | 40 | [[package]] 41 | category = "main" 42 | description = "High level compatibility layer for multiple asynchronous event loop implementations" 43 | name = "anyio" 44 | optional = false 45 | python-versions = ">=3.7" 46 | version = "3.7.1" 47 | 48 | [package.dependencies] 49 | idna = ">=2.8" 50 | sniffio = ">=1.1" 51 | 52 | [package.dependencies.exceptiongroup] 53 | python = "<3.11" 54 | version = "*" 55 | 56 | [package.extras] 57 | doc = ["packaging", "sphinx", "sphinx-rtd-theme (>=1.2.2)", "sphinxcontrib-jquery", "sphinx-autodoc-typehints (>=1.2.0)"] 58 | test = ["anyio", "coverage (>=4.5)", "hypothesis (>=4.0)", "psutil (>=5.9)", "pytest (>=7.0)", "pytest-mock (>=3.6.1)", "trustme", "uvloop (>=0.17)", "mock (>=4)"] 59 | trio = ["trio (<0.22)"] 60 | 61 | [[package]] 62 | category = "main" 63 | description = "Timeout context manager for asyncio programs" 64 | name = "async-timeout" 65 | optional = false 66 | python-versions = ">=3.7" 67 | version = "4.0.3" 68 | 69 | [[package]] 70 | category = "main" 71 | description = "Classes Without Boilerplate" 72 | name = "attrs" 73 | optional = false 74 | python-versions = ">=3.7" 75 | version = "23.1.0" 76 | 77 | [package.extras] 78 | cov = ["attrs", "coverage (>=5.3)"] 79 | dev = ["attrs", "pre-commit"] 80 | docs = ["furo", "myst-parser", "sphinx", "sphinx-notfound-page", "sphinxcontrib-towncrier", "towncrier", "zope-interface"] 81 | tests = ["attrs", "zope-interface"] 82 | tests-no-zope = ["cloudpickle", "hypothesis", "mypy (>=1.1.1)", "pympler", "pytest-mypy-plugins", "pytest-xdist", "pytest (>=4.3.0)"] 83 | 84 | [[package]] 85 | category = "dev" 86 | description = "The uncompromising code formatter." 87 | name = "black" 88 | optional = false 89 | python-versions = ">=3.8" 90 | version = "23.7.0" 91 | 92 | [package.dependencies] 93 | click = ">=8.0.0" 94 | mypy-extensions = ">=0.4.3" 95 | packaging = ">=22.0" 96 | pathspec = ">=0.9.0" 97 | platformdirs = ">=2" 98 | 99 | [package.dependencies.tomli] 100 | python = "<3.11" 101 | version = ">=1.1.0" 102 | 103 | [package.dependencies.typing-extensions] 104 | python = "<3.10" 105 | version = ">=3.10.0.0" 106 | 107 | [package.extras] 108 | colorama = ["colorama (>=0.4.3)"] 109 | d = ["aiohttp (>=3.7.4)"] 110 | jupyter = ["ipython (>=7.8.0)", "tokenize-rt (>=3.2.0)"] 111 | uvloop = ["uvloop (>=0.15.2)"] 112 | 113 | [[package]] 114 | category = "main" 115 | description = "Python package for providing Mozilla's CA Bundle." 116 | name = "certifi" 117 | optional = false 118 | python-versions = ">=3.6" 119 | version = "2023.7.22" 120 | 121 | [[package]] 122 | category = "main" 123 | description = "The Real First Universal Charset Detector. Open, modern and actively maintained alternative to Chardet." 124 | name = "charset-normalizer" 125 | optional = false 126 | python-versions = ">=3.7.0" 127 | version = "3.2.0" 128 | 129 | [[package]] 130 | category = "main" 131 | description = "Composable command line interface toolkit" 132 | name = "click" 133 | optional = false 134 | python-versions = ">=3.7" 135 | version = "8.1.7" 136 | 137 | [package.dependencies] 138 | colorama = "*" 139 | 140 | [[package]] 141 | category = "main" 142 | description = "Cross-platform colored terminal text." 143 | marker = "sys_platform == \"win32\" or platform_system == \"Windows\" or sys_platform == \"win32\"" 144 | name = "colorama" 145 | optional = false 146 | python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,!=3.6.*,>=2.7" 147 | version = "0.4.6" 148 | 149 | [[package]] 150 | category = "main" 151 | description = "Backport of PEP 654 (exception groups)" 152 | marker = "python_version < \"3.11\"" 153 | name = "exceptiongroup" 154 | optional = false 155 | python-versions = ">=3.7" 156 | version = "1.1.3" 157 | 158 | [package.extras] 159 | test = ["pytest (>=6)"] 160 | 161 | [[package]] 162 | category = "main" 163 | description = "FastAPI framework, high performance, easy to learn, fast to code, ready for production" 164 | name = "fastapi" 165 | optional = false 166 | python-versions = ">=3.7" 167 | version = "0.101.1" 168 | 169 | [package.dependencies] 170 | pydantic = ">=1.7.4,<1.8 || >1.8,<1.8.1 || >1.8.1,<2.0.0 || >2.0.0,<2.0.1 || >2.0.1,<2.1.0 || >2.1.0,<3.0.0" 171 | starlette = ">=0.27.0,<0.28.0" 172 | typing-extensions = ">=4.5.0" 173 | 174 | [package.extras] 175 | all = ["email-validator (>=2.0.0)", "httpx (>=0.23.0)", "itsdangerous (>=1.1.0)", "jinja2 (>=2.11.2)", "orjson (>=3.2.1)", "pydantic-extra-types (>=2.0.0)", "pydantic-settings (>=2.0.0)", "python-multipart (>=0.0.5)", "pyyaml (>=5.3.1)", "ujson (>=4.0.1,<4.0.2 || >4.0.2,<4.1.0 || >4.1.0,<4.2.0 || >4.2.0,<4.3.0 || >4.3.0,<5.0.0 || >5.0.0,<5.1.0 || >5.1.0)", "uvicorn (>=0.12.0)"] 176 | 177 | [[package]] 178 | category = "main" 179 | description = "A list-like structure which implements collections.abc.MutableSequence" 180 | name = "frozenlist" 181 | optional = false 182 | python-versions = ">=3.8" 183 | version = "1.4.0" 184 | 185 | [[package]] 186 | category = "main" 187 | description = "A pure-Python, bring-your-own-I/O implementation of HTTP/1.1" 188 | name = "h11" 189 | optional = false 190 | python-versions = ">=3.7" 191 | version = "0.14.0" 192 | 193 | [[package]] 194 | category = "main" 195 | description = "A minimal low-level HTTP client." 196 | name = "httpcore" 197 | optional = false 198 | python-versions = ">=3.7" 199 | version = "0.17.3" 200 | 201 | [package.dependencies] 202 | anyio = ">=3.0,<5.0" 203 | certifi = "*" 204 | h11 = ">=0.13,<0.15" 205 | sniffio = ">=1.0.0,<2.0.0" 206 | 207 | [package.extras] 208 | http2 = ["h2 (>=3,<5)"] 209 | socks = ["socksio (>=1.0.0,<2.0.0)"] 210 | 211 | [[package]] 212 | category = "main" 213 | description = "A collection of framework independent HTTP protocol utils." 214 | name = "httptools" 215 | optional = false 216 | python-versions = ">=3.5.0" 217 | version = "0.6.0" 218 | 219 | [package.extras] 220 | test = ["Cython (>=0.29.24,<0.30.0)"] 221 | 222 | [[package]] 223 | category = "main" 224 | description = "The next generation HTTP client." 225 | name = "httpx" 226 | optional = false 227 | python-versions = ">=3.7" 228 | version = "0.24.1" 229 | 230 | [package.dependencies] 231 | certifi = "*" 232 | httpcore = ">=0.15.0,<0.18.0" 233 | idna = "*" 234 | sniffio = "*" 235 | 236 | [package.extras] 237 | brotli = ["brotli", "brotlicffi"] 238 | cli = ["click (>=8.0.0,<9.0.0)", "pygments (>=2.0.0,<3.0.0)", "rich (>=10,<14)"] 239 | http2 = ["h2 (>=3,<5)"] 240 | socks = ["socksio (>=1.0.0,<2.0.0)"] 241 | 242 | [[package]] 243 | category = "main" 244 | description = "Internationalized Domain Names in Applications (IDNA)" 245 | name = "idna" 246 | optional = false 247 | python-versions = ">=3.5" 248 | version = "3.4" 249 | 250 | [[package]] 251 | category = "main" 252 | description = "brain-dead simple config-ini parsing" 253 | name = "iniconfig" 254 | optional = false 255 | python-versions = ">=3.7" 256 | version = "2.0.0" 257 | 258 | [[package]] 259 | category = "main" 260 | description = "multidict implementation" 261 | name = "multidict" 262 | optional = false 263 | python-versions = ">=3.7" 264 | version = "6.0.4" 265 | 266 | [[package]] 267 | category = "dev" 268 | description = "Type system extensions for programs checked with the mypy type checker." 269 | name = "mypy-extensions" 270 | optional = false 271 | python-versions = ">=3.5" 272 | version = "1.0.0" 273 | 274 | [[package]] 275 | category = "main" 276 | description = "Python client library for the OpenAI API" 277 | name = "openai" 278 | optional = false 279 | python-versions = ">=3.7.1" 280 | version = "0.27.8" 281 | 282 | [package.dependencies] 283 | aiohttp = "*" 284 | requests = ">=2.20" 285 | tqdm = "*" 286 | 287 | [package.extras] 288 | datalib = ["numpy", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "openpyxl (>=3.0.7)"] 289 | dev = ["black (>=21.6b0,<22.0.0)", "pytest (>=6.0.0,<7.0.0)", "pytest-asyncio", "pytest-mock"] 290 | embeddings = ["scikit-learn (>=1.0.2)", "tenacity (>=8.0.1)", "matplotlib", "plotly", "numpy", "scipy", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "openpyxl (>=3.0.7)"] 291 | wandb = ["wandb", "numpy", "pandas (>=1.2.3)", "pandas-stubs (>=1.1.0.11)", "openpyxl (>=3.0.7)"] 292 | 293 | [[package]] 294 | category = "main" 295 | description = "Core utilities for Python packages" 296 | name = "packaging" 297 | optional = false 298 | python-versions = ">=3.7" 299 | version = "23.1" 300 | 301 | [[package]] 302 | category = "dev" 303 | description = "Utility library for gitignore style pattern matching of file paths." 304 | name = "pathspec" 305 | optional = false 306 | python-versions = ">=3.7" 307 | version = "0.11.2" 308 | 309 | [[package]] 310 | category = "dev" 311 | description = "A small Python package for determining appropriate platform-specific dirs, e.g. a \"user data dir\"." 312 | name = "platformdirs" 313 | optional = false 314 | python-versions = ">=3.7" 315 | version = "3.10.0" 316 | 317 | [package.extras] 318 | docs = ["furo (>=2023.7.26)", "proselint (>=0.13)", "sphinx-autodoc-typehints (>=1.24)", "sphinx (>=7.1.1)"] 319 | test = ["appdirs (1.4.4)", "covdefaults (>=2.3)", "pytest-cov (>=4.1)", "pytest-mock (>=3.11.1)", "pytest (>=7.4)"] 320 | 321 | [[package]] 322 | category = "main" 323 | description = "plugin and hook calling mechanisms for python" 324 | name = "pluggy" 325 | optional = false 326 | python-versions = ">=3.8" 327 | version = "1.3.0" 328 | 329 | [package.extras] 330 | dev = ["pre-commit", "tox"] 331 | testing = ["pytest", "pytest-benchmark"] 332 | 333 | [[package]] 334 | category = "main" 335 | description = "Data validation using Python type hints" 336 | name = "pydantic" 337 | optional = false 338 | python-versions = ">=3.7" 339 | version = "2.2.1" 340 | 341 | [package.dependencies] 342 | annotated-types = ">=0.4.0" 343 | pydantic-core = "2.6.1" 344 | typing-extensions = ">=4.6.1" 345 | 346 | [package.extras] 347 | email = ["email-validator (>=2.0.0)"] 348 | 349 | [[package]] 350 | category = "main" 351 | description = "" 352 | name = "pydantic-core" 353 | optional = false 354 | python-versions = ">=3.7" 355 | version = "2.6.1" 356 | 357 | [package.dependencies] 358 | typing-extensions = ">=4.6.0,<4.7.0 || >4.7.0" 359 | 360 | [[package]] 361 | category = "main" 362 | description = "pytest: simple powerful testing with Python" 363 | name = "pytest" 364 | optional = false 365 | python-versions = ">=3.7" 366 | version = "7.4.2" 367 | 368 | [package.dependencies] 369 | colorama = "*" 370 | iniconfig = "*" 371 | packaging = "*" 372 | pluggy = ">=0.12,<2.0" 373 | 374 | [package.dependencies.exceptiongroup] 375 | python = "<3.11" 376 | version = ">=1.0.0rc8" 377 | 378 | [package.dependencies.tomli] 379 | python = "<3.11" 380 | version = ">=1.0.0" 381 | 382 | [package.extras] 383 | testing = ["argcomplete", "attrs (>=19.2.0)", "hypothesis (>=3.56)", "mock", "nose", "pygments (>=2.7.2)", "requests", "setuptools", "xmlschema"] 384 | 385 | [[package]] 386 | category = "main" 387 | description = "Strict separation of settings from code." 388 | name = "python-decouple" 389 | optional = false 390 | python-versions = "*" 391 | version = "3.8" 392 | 393 | [[package]] 394 | category = "main" 395 | description = "Read key-value pairs from a .env file and set them as environment variables" 396 | name = "python-dotenv" 397 | optional = false 398 | python-versions = ">=3.8" 399 | version = "1.0.0" 400 | 401 | [package.extras] 402 | cli = ["click (>=5.0)"] 403 | 404 | [[package]] 405 | category = "main" 406 | description = "YAML parser and emitter for Python" 407 | name = "pyyaml" 408 | optional = false 409 | python-versions = ">=3.6" 410 | version = "6.0.1" 411 | 412 | [[package]] 413 | category = "main" 414 | description = "Python HTTP for Humans." 415 | name = "requests" 416 | optional = false 417 | python-versions = ">=3.7" 418 | version = "2.31.0" 419 | 420 | [package.dependencies] 421 | certifi = ">=2017.4.17" 422 | charset-normalizer = ">=2,<4" 423 | idna = ">=2.5,<4" 424 | urllib3 = ">=1.21.1,<3" 425 | 426 | [package.extras] 427 | socks = ["PySocks (>=1.5.6,<1.5.7 || >1.5.7)"] 428 | use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"] 429 | 430 | [[package]] 431 | category = "main" 432 | description = "Sniff out which async library your code is running under" 433 | name = "sniffio" 434 | optional = false 435 | python-versions = ">=3.7" 436 | version = "1.3.0" 437 | 438 | [[package]] 439 | category = "main" 440 | description = "The little ASGI library that shines." 441 | name = "starlette" 442 | optional = false 443 | python-versions = ">=3.7" 444 | version = "0.27.0" 445 | 446 | [package.dependencies] 447 | anyio = ">=3.4.0,<5" 448 | 449 | [package.dependencies.typing-extensions] 450 | python = "<3.10" 451 | version = ">=3.10.0" 452 | 453 | [package.extras] 454 | full = ["httpx (>=0.22.0)", "itsdangerous", "jinja2", "python-multipart", "pyyaml"] 455 | 456 | [[package]] 457 | category = "main" 458 | description = "A lil' TOML parser" 459 | marker = "python_version < \"3.11\"" 460 | name = "tomli" 461 | optional = false 462 | python-versions = ">=3.7" 463 | version = "2.0.1" 464 | 465 | [[package]] 466 | category = "main" 467 | description = "Fast, Extensible Progress Meter" 468 | name = "tqdm" 469 | optional = false 470 | python-versions = ">=3.7" 471 | version = "4.66.1" 472 | 473 | [package.dependencies] 474 | colorama = "*" 475 | 476 | [package.extras] 477 | dev = ["pytest (>=6)", "pytest-cov", "pytest-timeout", "pytest-xdist"] 478 | notebook = ["ipywidgets (>=6)"] 479 | slack = ["slack-sdk"] 480 | telegram = ["requests"] 481 | 482 | [[package]] 483 | category = "main" 484 | description = "Backported and Experimental Type Hints for Python 3.7+" 485 | name = "typing-extensions" 486 | optional = false 487 | python-versions = ">=3.7" 488 | version = "4.7.1" 489 | 490 | [[package]] 491 | category = "main" 492 | description = "HTTP library with thread-safe connection pooling, file post, and more." 493 | name = "urllib3" 494 | optional = false 495 | python-versions = ">=3.7" 496 | version = "2.0.4" 497 | 498 | [package.extras] 499 | brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)"] 500 | secure = ["certifi", "cryptography (>=1.9)", "idna (>=2.0.0)", "pyopenssl (>=17.1.0)", "urllib3-secure-extra"] 501 | socks = ["pysocks (>=1.5.6,<1.5.7 || >1.5.7,<2.0)"] 502 | zstd = ["zstandard (>=0.18.0)"] 503 | 504 | [[package]] 505 | category = "main" 506 | description = "The lightning-fast ASGI server." 507 | name = "uvicorn" 508 | optional = false 509 | python-versions = ">=3.8" 510 | version = "0.23.2" 511 | 512 | [package.dependencies] 513 | click = ">=7.0" 514 | h11 = ">=0.8" 515 | 516 | [package.dependencies.colorama] 517 | optional = true 518 | version = ">=0.4" 519 | 520 | [package.dependencies.httptools] 521 | optional = true 522 | version = ">=0.5.0" 523 | 524 | [package.dependencies.python-dotenv] 525 | optional = true 526 | version = ">=0.13" 527 | 528 | [package.dependencies.pyyaml] 529 | optional = true 530 | version = ">=5.1" 531 | 532 | [package.dependencies.typing-extensions] 533 | python = "<3.11" 534 | version = ">=4.0" 535 | 536 | [package.dependencies.uvloop] 537 | optional = true 538 | version = ">=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1" 539 | 540 | [package.dependencies.watchfiles] 541 | optional = true 542 | version = ">=0.13" 543 | 544 | [package.dependencies.websockets] 545 | optional = true 546 | version = ">=10.4" 547 | 548 | [package.extras] 549 | standard = ["colorama (>=0.4)", "httptools (>=0.5.0)", "python-dotenv (>=0.13)", "pyyaml (>=5.1)", "uvloop (>=0.14.0,<0.15.0 || >0.15.0,<0.15.1 || >0.15.1)", "watchfiles (>=0.13)", "websockets (>=10.4)"] 550 | 551 | [[package]] 552 | category = "main" 553 | description = "Fast implementation of asyncio event loop on top of libuv" 554 | marker = "sys_platform != \"win32\" and sys_platform != \"cygwin\" and platform_python_implementation != \"PyPy\"" 555 | name = "uvloop" 556 | optional = false 557 | python-versions = ">=3.7" 558 | version = "0.17.0" 559 | 560 | [package.extras] 561 | dev = ["Cython (>=0.29.32,<0.30.0)", "pytest (>=3.6.0)", "Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)", "flake8 (>=3.9.2,<3.10.0)", "psutil", "pycodestyle (>=2.7.0,<2.8.0)", "pyOpenSSL (>=22.0.0,<22.1.0)", "mypy (>=0.800)", "aiohttp"] 562 | docs = ["Sphinx (>=4.1.2,<4.2.0)", "sphinxcontrib-asyncio (>=0.3.0,<0.4.0)", "sphinx-rtd-theme (>=0.5.2,<0.6.0)"] 563 | test = ["flake8 (>=3.9.2,<3.10.0)", "psutil", "pycodestyle (>=2.7.0,<2.8.0)", "pyOpenSSL (>=22.0.0,<22.1.0)", "mypy (>=0.800)", "Cython (>=0.29.32,<0.30.0)", "aiohttp"] 564 | 565 | [[package]] 566 | category = "main" 567 | description = "Simple, modern and high performance file watching and code reload in python." 568 | name = "watchfiles" 569 | optional = false 570 | python-versions = ">=3.7" 571 | version = "0.19.0" 572 | 573 | [package.dependencies] 574 | anyio = ">=3.0.0" 575 | 576 | [[package]] 577 | category = "main" 578 | description = "An implementation of the WebSocket Protocol (RFC 6455 & 7692)" 579 | name = "websockets" 580 | optional = false 581 | python-versions = ">=3.7" 582 | version = "11.0.3" 583 | 584 | [[package]] 585 | category = "main" 586 | description = "Yet another URL library" 587 | name = "yarl" 588 | optional = false 589 | python-versions = ">=3.7" 590 | version = "1.9.2" 591 | 592 | [package.dependencies] 593 | idna = ">=2.0" 594 | multidict = ">=4.0" 595 | 596 | [metadata] 597 | content-hash = "7518e2b0b6df2ad4e4520cf912214061b36afaaf2eac129685fa443b189f3c45" 598 | lock-version = "1.0" 599 | python-versions = "^3.9" 600 | 601 | [metadata.files] 602 | aiohttp = [] 603 | aiosignal = [] 604 | annotated-types = [] 605 | anyio = [] 606 | async-timeout = [] 607 | attrs = [] 608 | black = [] 609 | certifi = [] 610 | charset-normalizer = [] 611 | click = [] 612 | colorama = [] 613 | exceptiongroup = [] 614 | fastapi = [] 615 | frozenlist = [] 616 | h11 = [] 617 | httpcore = [] 618 | httptools = [] 619 | httpx = [] 620 | idna = [] 621 | iniconfig = [] 622 | multidict = [] 623 | mypy-extensions = [] 624 | openai = [] 625 | packaging = [] 626 | pathspec = [] 627 | platformdirs = [] 628 | pluggy = [] 629 | pydantic = [] 630 | pydantic-core = [] 631 | pytest = [] 632 | python-decouple = [] 633 | python-dotenv = [] 634 | pyyaml = [] 635 | requests = [] 636 | sniffio = [] 637 | starlette = [] 638 | tomli = [] 639 | tqdm = [] 640 | typing-extensions = [] 641 | urllib3 = [] 642 | uvicorn = [] 643 | uvloop = [] 644 | watchfiles = [] 645 | websockets = [] 646 | yarl = [] 647 | -------------------------------------------------------------------------------- /pyproject.toml: -------------------------------------------------------------------------------- 1 | [tool.poetry] 2 | name = "geogpt" 3 | version = "0.1.0" 4 | description = "" 5 | authors = ["sorieux "] 6 | 7 | [tool.poetry.dependencies] 8 | python = "^3.9" 9 | openai = "^0.27.8" 10 | fastapi = "^0.101.1" 11 | uvicorn = {extras = ["standard"], version = "^0.23.2"} 12 | python-decouple = "^3.8" 13 | httpx = "^0.24.1" 14 | 15 | 16 | [tool.poetry.dev-dependencies] 17 | black = "^23.7.0" 18 | pytest = "^7.4.2" 19 | 20 | [build-system] 21 | requires = ["poetry>=0.12"] 22 | build-backend = "poetry.masonry.api" 23 | --------------------------------------------------------------------------------