minor update for supporting 3rd party Open AI APIs + add Ollama (#376)

This commit is contained in:
Chih-Yu Yeh
2024-06-26 16:09:41 +08:00
committed by GitHub
parent ac0f67c448
commit 53bda673a8
32 changed files with 669 additions and 238 deletions
+1 -1
View File
@@ -52,7 +52,7 @@ jobs:
LLM_PROVIDER: openai
DOCUMENT_STORE_PROVIDER: qdrant
OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
OPENAI_GENERATION_MODEL: gpt-3.5-turbo
GENERATION_MODEL: gpt-3.5-turbo
WREN_ENGINE_ENDPOINT: http://localhost:8080
WREN_UI_ENDPOINT: http://localhost:3000
QDRANT_HOST: localhost
+1
View File
@@ -72,6 +72,7 @@ yarn-error.log*
## local env files
.env*.local
.env.ai
## vercel
.vercel
+1 -1
View File
@@ -19,7 +19,7 @@ data:
WREN_UI_VERSION: "0.8.0"
# OpenAI
OPENAI_GENERATION_MODEL: "gpt-3.5-turbo"
GENERATION_MODEL: "gpt-3.5-turbo"
# Telemetry
POSTHOG_HOST: "https://app.posthog.com"
@@ -36,11 +36,11 @@ spec:
configMapKeyRef:
name: wren-config
key: OPENAI_API_BASE
- name: OPENAI_GENERATION_MODEL
valueFrom:
- name: GENERATION_MODEL
valueFrom:
configMapKeyRef:
name: wren-config
key: OPENAI_GENERATION_MODEL
key: GENERATION_MODEL
- name: QDRANT_HOST
valueFrom:
secretKeyRef:
@@ -44,11 +44,11 @@ spec:
configMapKeyRef:
name: wren-config
key: WREN_AI_ENDPOINT
- name: OPENAI_GENERATION_MODEL
- name: GENERATION_MODEL
valueFrom:
configMapKeyRef:
name: wren-config
key: OPENAI_GENERATION_MODEL
key: GENERATION_MODEL
- name: PG_URL
valueFrom:
secretKeyRef:
+1 -1
View File
@@ -15,7 +15,7 @@
WREN_UI_VERSION: "0.8.0"
# OpenAI
OPENAI_GENERATION_MODEL: "gpt-3.5-turbo"
GENERATION_MODEL: "gpt-3.5-turbo"
# Telemetry
POSTHOG_HOST: "https://app.posthog.com"
+28
View File
@@ -0,0 +1,28 @@
## LLM
LLM_PROVIDER= # openai, azure-openai, ollama
# openai or openai-api-compatible llm
OPENAI_API_KEY=
OPENAI_API_BASE=
# azure-openai
AZURE_CHAT_BASE=
AZURE_CHAT_KEY=
AZURE_CHAT_VERSION=
AZURE_EMBED_BASE=
AZURE_EMBED_KEY=
AZURE_EMBED_VERSION=
# ollama
OLLAMA_URL=http://host.docker.internal:11434
GENERATION_MODEL=
EMBEDDING_MODEL=
EMBEDDING_MODEL_DIMENSION=
## DOCUMENT_STORE
DOCUMENT_STORE_PROVIDER=qdrant
QDRANT_HOST=qdrant
+4 -17
View File
@@ -11,6 +11,10 @@ IBIS_SERVER_PORT=8000
# service endpoint (for docker-compose-dev.yaml file)
WREN_UI_ENDPOINT=http://docker.for.mac.localhost:3000
# LLM
OPENAI_API_KEY=
GENERATION_MODEL=gpt-3.5-turbo # gpt-3.5-turbo, gpt-4o, gpt-4-turbo
# version
# CHANGE THIS TO THE LATEST VERSION
WREN_PRODUCT_VERSION=0.5.0
@@ -20,23 +24,6 @@ WREN_UI_VERSION=0.8.0
IBIS_SERVER_VERSION=0.5.1
WREN_BOOTSTRAP_VERSION=0.1.4
# keys
LLM_PROVIDER=openai
DOCUMENT_STORE_PROVIDER=qdrant
# CHANGE THIS TO YOUR OPENAI API KEY
OPENAI_API_KEY=sk-1234567890
OPENAI_API_BASE=https://api.openai.com/v1
OPENAI_GENERATION_MODEL=gpt-3.5-turbo
# Azure env
AZURE_CHAT_BASE=
AZURE_CHAT_KEY=
AZURE_CHAT_VERSION=
AZURE_EMBED_BASE=
AZURE_EMBED_KEY=
AZURE_EMBED_VERSION=
# AI service related env variables
AI_SERVICE_ENABLE_TIMER=
AI_SERVICE_LOGGING_LEVEL=INFO
+8 -2
View File
@@ -20,5 +20,11 @@ Path structure as following:
## How to start
1. copy `.env.example` to `.env.local` and modify the OpenAI API key.
1. (optional) if your port 3000 is occupied, you can modify the `HOST_PORT` in `.env.local`.
1. run `docker-compose --env-file .env.local up` to start all services.
2. (optional) copy `.env.ai.example` to `.env.ai` and fill in necessary information if you would like to use custom LLM.
3. (optional) if your port 3000 is occupied, you can modify the `HOST_PORT` in `.env.example`.
4. start all services:
- using OpenAI: `docker-compose --env-file .env.local up -d`
- using custom LLM: `docker-compose --env-file .env.local --env-file .env.ai up -d`
5. stop all services:
- using OpenAI: `docker-compose --env-file .env.local down`
- using custom LLM: `docker-compose --env-file .env.local --env-file .env.ai down`
+2 -6
View File
@@ -42,13 +42,9 @@ services:
ports:
- ${AI_SERVICE_FORWARD_PORT}:${WREN_AI_SERVICE_PORT}
environment:
WREN_AI_SERVICE_PORT: ${WREN_AI_SERVICE_PORT}
LLM_PROVIDER: ${LLM_PROVIDER}
DOCUMENT_STORE_PROVIDER: ${DOCUMENT_STORE_PROVIDER}
OPENAI_API_KEY: ${OPENAI_API_KEY}
OPENAI_GENERATION_MODEL: ${OPENAI_GENERATION_MODEL}
QDRANT_HOST: qdrant
WREN_UI_ENDPOINT: ${WREN_UI_ENDPOINT}
OPENAI_API_KEY: ${OPENAI_API_KEY}
GENERATION_MODEL: ${GENERATION_MODEL}
ENABLE_TIMER: ${AI_SERVICE_ENABLE_TIMER}
LOGGING_LEVEL: ${AI_SERVICE_LOGGING_LEVEL}
# sometimes the console won't show print messages,
+5 -7
View File
@@ -55,13 +55,9 @@ services:
- ${AI_SERVICE_FORWARD_PORT}:${WREN_AI_SERVICE_PORT}
environment:
WREN_AI_SERVICE_PORT: ${WREN_AI_SERVICE_PORT}
LLM_PROVIDER: ${LLM_PROVIDER}
DOCUMENT_STORE_PROVIDER: ${DOCUMENT_STORE_PROVIDER}
OPENAI_API_KEY: ${OPENAI_API_KEY}
OPENAI_API_BASE: ${OPENAI_API_BASE}
OPENAI_GENERATION_MODEL: ${OPENAI_GENERATION_MODEL}
QDRANT_HOST: qdrant
WREN_UI_ENDPOINT: http://wren-ui:${WREN_UI_PORT}
OPENAI_API_KEY: ${OPENAI_API_KEY}
GENERATION_MODEL: ${GENERATION_MODEL}
ENABLE_TIMER: ${AI_SERVICE_ENABLE_TIMER}
LOGGING_LEVEL: ${AI_SERVICE_LOGGING_LEVEL}
# sometimes the console won't show print messages,
@@ -95,7 +91,9 @@ services:
WREN_ENGINE_ENDPOINT: http://wren-engine:${WREN_ENGINE_PORT}
WREN_AI_ENDPOINT: http://wren-ai-service:${WREN_AI_SERVICE_PORT}
IBIS_SERVER_ENDPOINT: http://ibis-server:${IBIS_SERVER_PORT}
OPENAI_GENERATION_MODEL: ${OPENAI_GENERATION_MODEL}
EMBEDDING_MODEL: ${EMBEDDING_MODEL}
EMBEDDING_MODEL_DIM: ${EMBEDDING_MODEL_DIM}
GENERATION_MODEL: ${GENERATION_MODEL}
PG_USERNAME: ${PG_USERNAME}
PG_PASSWORD: ${PG_PASSWORD}
# telemetry
+24 -14
View File
@@ -1,21 +1,17 @@
# fastapi related
# app related
WREN_AI_SERVICE_HOST=127.0.0.1
WREN_AI_SERVICE_PORT=5556
WREN_ENGINE_ENDPOINT=http://localhost:8080
WREN_UI_ENDPOINT=http://localhost:3000
# app related
# LLM Provider name should be mapped to Haystack's supported LLM providers: https://docs.haystack.deepset.ai/v2.0/docs/generators
LLM_PROVIDER=openai # azure_openai as well
## LLM
LLM_PROVIDER=openai # openai, azure-openai, ollama
# Document Store Provider name should be mapped to Haystack's supported Document Store providers: see the haystack documentation's left sidebar
DOCUMENT_STORE_PROVIDER=qdrant
# llm provider specific env variables names must be in the format of [LLM_PROVIDER]_[ENV_VARIABLE_NAME] and must be in uppercase
OPENAI_API_KEY=
# openai or openai-api-compatible llm
OPENAI_API_KEY=sk-1234567890
OPENAI_API_BASE=https://api.openai.com/v1
OPENAI_GENERATION_MODEL=gpt-3.5-turbo # gpt-4o, gpt-4-turbo, gpt-3.5-turbo
#Azure openai env
# azure-openai
AZURE_CHAT_BASE=
AZURE_CHAT_KEY=
AZURE_CHAT_VERSION=
@@ -24,8 +20,17 @@ AZURE_EMBED_BASE=
AZURE_EMBED_KEY=
AZURE_EMBED_VERSION=
# document store provider specific env variables names must be in the format of [DOCUMENT_STORE_PROVIDER]_[ENV_VARIABLE_NAME] and must be in uppercase
QDRANT_HOST=localhost
# ollama
OLLAMA_URL=http://localhost:11434
GENERATION_MODEL=gpt-3.5-turbo
EMBEDDING_MODEL=text-embedding-3-large
EMBEDDING_MODEL_DIMENSION=3072
## DOCUMENT_STORE
DOCUMENT_STORE_PROVIDER=qdrant
QDRANT_HOST=http://localhost:6333
ENGINE=wren-ui
@@ -43,3 +48,8 @@ DATASET_NAME=book_2
ENABLE_TIMER=
LOGGING_LEVEL=INFO
LANGFUSE_ENABLE=
LANGFUSE_SECRET_KEY=
LANGFUSE_PUBLIC_KEY=
LANGFUSE_HOST=https://cloud.langfuse.com
+3 -1
View File
@@ -20,7 +20,9 @@ DOCUMENT_STORE_PROVIDER=qdrant
# llm provider specific env variables names must be in the format of [LLM_PROVIDER]_[ENV_VARIABLE_NAME] and must be in uppercase
OPENAI_API_KEY=
OPENAI_API_BASE=https://api.openai.com/v1
OPENAI_GENERATION_MODEL=gpt-3.5-turbo # gpt-4o, gpt-4-turbo, gpt-3.5-turbo
EMBEDDING_MODEL=text-embedding-3-large
EMBEDDING_MODEL_DIMENSION=3072
GENERATION_MODEL=gpt-3.5-turbo # gpt-4o, gpt-4-turbo, gpt-3.5-turbo
#Azure openai env
AZURE_CHAT_BASE=
+1 -1
View File
@@ -14,7 +14,7 @@ services:
WREN_AI_SERVICE_PORT: ${WREN_AI_SERVICE_PORT}
OPENAI_API_KEY: ${OPENAI_API_KEY}
OPENAI_API_BASE: ${OPENAI_API_BASE}
OPENAI_GENERATION_MODEL: ${OPENAI_GENERATION_MODEL}
GENERATION_MODEL: ${GENERATION_MODEL}
QDRANT_HOST: ${QDRANT_HOST}
WREN_UI_ENDPOINT: ${WREN_UI_ENDPOINT}
ENABLE_TIMER: ${ENABLE_TIMER}
+100 -84
View File
@@ -1751,22 +1751,22 @@ tenacity = ">=8.1.0,<9.0.0"
[[package]]
name = "langchain-core"
version = "0.2.7"
version = "0.2.9"
description = "Building applications with LLMs through composability"
optional = false
python-versions = "<4.0,>=3.8.1"
files = [
{file = "langchain_core-0.2.7-py3-none-any.whl", hash = "sha256:fd02e153c898486dd728d634684ffc64bc257ff2ba443dc7e53d017ac0bf4658"},
{file = "langchain_core-0.2.7.tar.gz", hash = "sha256:b0b1b6dfbdedb39426fcb8bd3f07e40eec7964856e3fc384c420ca6dba61b34e"},
{file = "langchain_core-0.2.9-py3-none-any.whl", hash = "sha256:426a5a4fea95a5db995ba5ab560b76edd4998fb6fe52ccc28ac987092a4cbfcd"},
{file = "langchain_core-0.2.9.tar.gz", hash = "sha256:f1c59082642921727844e1cd0eb36d451edd1872c20e193aa3142aac03495986"},
]
[package.dependencies]
jsonpatch = ">=1.33,<2.0"
langsmith = ">=0.1.75,<0.2.0"
packaging = ">=23.2,<25"
pydantic = ">=1,<3"
pydantic = {version = ">=1,<3", markers = "python_full_version < \"3.12.4\""}
PyYAML = ">=5.3"
tenacity = ">=8.1.0,<9.0.0"
tenacity = ">=8.1.0,<8.4.0 || >8.4.0,<9.0.0"
[[package]]
name = "langchain-openai"
@@ -1803,13 +1803,13 @@ extended-testing = ["beautifulsoup4 (>=4.12.3,<5.0.0)", "lxml (>=4.9.3,<6.0)"]
[[package]]
name = "langsmith"
version = "0.1.77"
version = "0.1.80"
description = "Client library to connect to the LangSmith LLM Tracing and Evaluation Platform."
optional = false
python-versions = "<4.0,>=3.8.1"
files = [
{file = "langsmith-0.1.77-py3-none-any.whl", hash = "sha256:2202cc21b1ed7e7b9e5d2af2694be28898afa048c09fdf09f620cbd9301755ae"},
{file = "langsmith-0.1.77.tar.gz", hash = "sha256:4ace09077a9a4e412afeb4b517ca68e7de7b07f36e4792dc8236ac5207c0c0c7"},
{file = "langsmith-0.1.80-py3-none-any.whl", hash = "sha256:951fc29576b52afd8378d41f6db343090fea863e3620f0ca97e83b221f93c94d"},
{file = "langsmith-0.1.80.tar.gz", hash = "sha256:a29b1dde27612308beee424f1388ad844c8e7e375bf2ac8bdf4da174013f279d"},
]
[package.dependencies]
@@ -2287,6 +2287,21 @@ files = [
{file = "numpy-1.26.4.tar.gz", hash = "sha256:2a02aba9ed12e4ac4eb3ea9421c420301a0c6460d9830d74a9df87efa4912010"},
]
[[package]]
name = "ollama-haystack"
version = "0.0.6"
description = "An integration between the Ollama LLM framework and Haystack"
optional = false
python-versions = ">=3.8"
files = [
{file = "ollama_haystack-0.0.6-py3-none-any.whl", hash = "sha256:a1bc20367e7da76485a9997c53d1898176819505038633a4ef38344c116f13c0"},
{file = "ollama_haystack-0.0.6.tar.gz", hash = "sha256:12609538bc0c61d624a8314390c717c00c6c07373714e0f09420b97b1e91e23c"},
]
[package.dependencies]
haystack-ai = "*"
requests = "*"
[[package]]
name = "openai"
version = "1.30.1"
@@ -2644,27 +2659,28 @@ files = [
[[package]]
name = "psutil"
version = "5.9.8"
version = "6.0.0"
description = "Cross-platform lib for process and system monitoring in Python."
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*"
python-versions = "!=3.0.*,!=3.1.*,!=3.2.*,!=3.3.*,!=3.4.*,!=3.5.*,>=2.7"
files = [
{file = "psutil-5.9.8-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:26bd09967ae00920df88e0352a91cff1a78f8d69b3ecabbfe733610c0af486c8"},
{file = "psutil-5.9.8-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:05806de88103b25903dff19bb6692bd2e714ccf9e668d050d144012055cbca73"},
{file = "psutil-5.9.8-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:611052c4bc70432ec770d5d54f64206aa7203a101ec273a0cd82418c86503bb7"},
{file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:50187900d73c1381ba1454cf40308c2bf6f34268518b3f36a9b663ca87e65e36"},
{file = "psutil-5.9.8-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:02615ed8c5ea222323408ceba16c60e99c3f91639b07da6373fb7e6539abc56d"},
{file = "psutil-5.9.8-cp27-none-win32.whl", hash = "sha256:36f435891adb138ed3c9e58c6af3e2e6ca9ac2f365efe1f9cfef2794e6c93b4e"},
{file = "psutil-5.9.8-cp27-none-win_amd64.whl", hash = "sha256:bd1184ceb3f87651a67b2708d4c3338e9b10c5df903f2e3776b62303b26cb631"},
{file = "psutil-5.9.8-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:aee678c8720623dc456fa20659af736241f575d79429a0e5e9cf88ae0605cc81"},
{file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:8cb6403ce6d8e047495a701dc7c5bd788add903f8986d523e3e20b98b733e421"},
{file = "psutil-5.9.8-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:d06016f7f8625a1825ba3732081d77c94589dca78b7a3fc072194851e88461a4"},
{file = "psutil-5.9.8-cp36-cp36m-win32.whl", hash = "sha256:7d79560ad97af658a0f6adfef8b834b53f64746d45b403f225b85c5c2c140eee"},
{file = "psutil-5.9.8-cp36-cp36m-win_amd64.whl", hash = "sha256:27cc40c3493bb10de1be4b3f07cae4c010ce715290a5be22b98493509c6299e2"},
{file = "psutil-5.9.8-cp37-abi3-win32.whl", hash = "sha256:bc56c2a1b0d15aa3eaa5a60c9f3f8e3e565303b465dbf57a1b730e7a2b9844e0"},
{file = "psutil-5.9.8-cp37-abi3-win_amd64.whl", hash = "sha256:8db4c1b57507eef143a15a6884ca10f7c73876cdf5d51e713151c1236a0e68cf"},
{file = "psutil-5.9.8-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:d16bbddf0693323b8c6123dd804100241da461e41d6e332fb0ba6058f630f8c8"},
{file = "psutil-5.9.8.tar.gz", hash = "sha256:6be126e3225486dff286a8fb9a06246a5253f4c7c53b475ea5f5ac934e64194c"},
{file = "psutil-6.0.0-cp27-cp27m-macosx_10_9_x86_64.whl", hash = "sha256:a021da3e881cd935e64a3d0a20983bda0bb4cf80e4f74fa9bfcb1bc5785360c6"},
{file = "psutil-6.0.0-cp27-cp27m-manylinux2010_i686.whl", hash = "sha256:1287c2b95f1c0a364d23bc6f2ea2365a8d4d9b726a3be7294296ff7ba97c17f0"},
{file = "psutil-6.0.0-cp27-cp27m-manylinux2010_x86_64.whl", hash = "sha256:a9a3dbfb4de4f18174528d87cc352d1f788b7496991cca33c6996f40c9e3c92c"},
{file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_i686.whl", hash = "sha256:6ec7588fb3ddaec7344a825afe298db83fe01bfaaab39155fa84cf1c0d6b13c3"},
{file = "psutil-6.0.0-cp27-cp27mu-manylinux2010_x86_64.whl", hash = "sha256:1e7c870afcb7d91fdea2b37c24aeb08f98b6d67257a5cb0a8bc3ac68d0f1a68c"},
{file = "psutil-6.0.0-cp27-none-win32.whl", hash = "sha256:02b69001f44cc73c1c5279d02b30a817e339ceb258ad75997325e0e6169d8b35"},
{file = "psutil-6.0.0-cp27-none-win_amd64.whl", hash = "sha256:21f1fb635deccd510f69f485b87433460a603919b45e2a324ad65b0cc74f8fb1"},
{file = "psutil-6.0.0-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:c588a7e9b1173b6e866756dde596fd4cad94f9399daf99ad8c3258b3cb2b47a0"},
{file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_i686.manylinux2010_i686.manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:6ed2440ada7ef7d0d608f20ad89a04ec47d2d3ab7190896cd62ca5fc4fe08bf0"},
{file = "psutil-6.0.0-cp36-abi3-manylinux_2_12_x86_64.manylinux2010_x86_64.manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:5fd9a97c8e94059b0ef54a7d4baf13b405011176c3b6ff257c247cae0d560ecd"},
{file = "psutil-6.0.0-cp36-abi3-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:e2e8d0054fc88153ca0544f5c4d554d42e33df2e009c4ff42284ac9ebdef4132"},
{file = "psutil-6.0.0-cp36-cp36m-win32.whl", hash = "sha256:fc8c9510cde0146432bbdb433322861ee8c3efbf8589865c8bf8d21cb30c4d14"},
{file = "psutil-6.0.0-cp36-cp36m-win_amd64.whl", hash = "sha256:34859b8d8f423b86e4385ff3665d3f4d94be3cdf48221fbe476e883514fdb71c"},
{file = "psutil-6.0.0-cp37-abi3-win32.whl", hash = "sha256:a495580d6bae27291324fe60cea0b5a7c23fa36a7cd35035a16d93bdcf076b9d"},
{file = "psutil-6.0.0-cp37-abi3-win_amd64.whl", hash = "sha256:33ea5e1c975250a720b3a6609c490db40dae5d83a4eb315170c4fe0d8b1f34b3"},
{file = "psutil-6.0.0-cp38-abi3-macosx_11_0_arm64.whl", hash = "sha256:ffe7fc9b6b36beadc8c322f84e1caff51e8703b88eee1da46d1e3a6ae11b4fd0"},
{file = "psutil-6.0.0.tar.gz", hash = "sha256:8faae4f310b6d969fa26ca0545338b21f73c6b15db7c4a8d934a5482faa818f2"},
]
[package.extras]
@@ -3677,64 +3693,64 @@ files = [
[[package]]
name = "sqlalchemy"
version = "2.0.30"
version = "2.0.31"
description = "Database Abstraction Library"
optional = false
python-versions = ">=3.7"
files = [
{file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:3b48154678e76445c7ded1896715ce05319f74b1e73cf82d4f8b59b46e9c0ddc"},
{file = "SQLAlchemy-2.0.30-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:2753743c2afd061bb95a61a51bbb6a1a11ac1c44292fad898f10c9839a7f75b2"},
{file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:a7bfc726d167f425d4c16269a9a10fe8630ff6d14b683d588044dcef2d0f6be7"},
{file = "SQLAlchemy-2.0.30-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c4f61ada6979223013d9ab83a3ed003ded6959eae37d0d685db2c147e9143797"},
{file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_aarch64.whl", hash = "sha256:3a365eda439b7a00732638f11072907c1bc8e351c7665e7e5da91b169af794af"},
{file = "SQLAlchemy-2.0.30-cp310-cp310-musllinux_1_1_x86_64.whl", hash = "sha256:bba002a9447b291548e8d66fd8c96a6a7ed4f2def0bb155f4f0a1309fd2735d5"},
{file = "SQLAlchemy-2.0.30-cp310-cp310-win32.whl", hash = "sha256:0138c5c16be3600923fa2169532205d18891b28afa817cb49b50e08f62198bb8"},
{file = "SQLAlchemy-2.0.30-cp310-cp310-win_amd64.whl", hash = "sha256:99650e9f4cf3ad0d409fed3eec4f071fadd032e9a5edc7270cd646a26446feeb"},
{file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:955991a09f0992c68a499791a753523f50f71a6885531568404fa0f231832aa0"},
{file = "SQLAlchemy-2.0.30-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:f69e4c756ee2686767eb80f94c0125c8b0a0b87ede03eacc5c8ae3b54b99dc46"},
{file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:69c9db1ce00e59e8dd09d7bae852a9add716efdc070a3e2068377e6ff0d6fdaa"},
{file = "SQLAlchemy-2.0.30-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:a1429a4b0f709f19ff3b0cf13675b2b9bfa8a7e79990003207a011c0db880a13"},
{file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_aarch64.whl", hash = "sha256:efedba7e13aa9a6c8407c48facfdfa108a5a4128e35f4c68f20c3407e4376aa9"},
{file = "SQLAlchemy-2.0.30-cp311-cp311-musllinux_1_1_x86_64.whl", hash = "sha256:16863e2b132b761891d6c49f0a0f70030e0bcac4fd208117f6b7e053e68668d0"},
{file = "SQLAlchemy-2.0.30-cp311-cp311-win32.whl", hash = "sha256:2ecabd9ccaa6e914e3dbb2aa46b76dede7eadc8cbf1b8083c94d936bcd5ffb49"},
{file = "SQLAlchemy-2.0.30-cp311-cp311-win_amd64.whl", hash = "sha256:0b3f4c438e37d22b83e640f825ef0f37b95db9aa2d68203f2c9549375d0b2260"},
{file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:5a79d65395ac5e6b0c2890935bad892eabb911c4aa8e8015067ddb37eea3d56c"},
{file = "SQLAlchemy-2.0.30-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:9a5baf9267b752390252889f0c802ea13b52dfee5e369527da229189b8bd592e"},
{file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:3cb5a646930c5123f8461f6468901573f334c2c63c795b9af350063a736d0134"},
{file = "SQLAlchemy-2.0.30-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:296230899df0b77dec4eb799bcea6fbe39a43707ce7bb166519c97b583cfcab3"},
{file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_aarch64.whl", hash = "sha256:c62d401223f468eb4da32627bffc0c78ed516b03bb8a34a58be54d618b74d472"},
{file = "SQLAlchemy-2.0.30-cp312-cp312-musllinux_1_1_x86_64.whl", hash = "sha256:3b69e934f0f2b677ec111b4d83f92dc1a3210a779f69bf905273192cf4ed433e"},
{file = "SQLAlchemy-2.0.30-cp312-cp312-win32.whl", hash = "sha256:77d2edb1f54aff37e3318f611637171e8ec71472f1fdc7348b41dcb226f93d90"},
{file = "SQLAlchemy-2.0.30-cp312-cp312-win_amd64.whl", hash = "sha256:b6c7ec2b1f4969fc19b65b7059ed00497e25f54069407a8701091beb69e591a5"},
{file = "SQLAlchemy-2.0.30-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:5a8e3b0a7e09e94be7510d1661339d6b52daf202ed2f5b1f9f48ea34ee6f2d57"},
{file = "SQLAlchemy-2.0.30-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:b60203c63e8f984df92035610c5fb76d941254cf5d19751faab7d33b21e5ddc0"},
{file = "SQLAlchemy-2.0.30-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:f1dc3eabd8c0232ee8387fbe03e0a62220a6f089e278b1f0aaf5e2d6210741ad"},
{file = "SQLAlchemy-2.0.30-cp37-cp37m-musllinux_1_1_aarch64.whl", hash = "sha256:40ad017c672c00b9b663fcfcd5f0864a0a97828e2ee7ab0c140dc84058d194cf"},
{file = "SQLAlchemy-2.0.30-cp37-cp37m-musllinux_1_1_x86_64.whl", hash = "sha256:e42203d8d20dc704604862977b1470a122e4892791fe3ed165f041e4bf447a1b"},
{file = "SQLAlchemy-2.0.30-cp37-cp37m-win32.whl", hash = "sha256:2a4f4da89c74435f2bc61878cd08f3646b699e7d2eba97144030d1be44e27584"},
{file = "SQLAlchemy-2.0.30-cp37-cp37m-win_amd64.whl", hash = "sha256:b6bf767d14b77f6a18b6982cbbf29d71bede087edae495d11ab358280f304d8e"},
{file = "SQLAlchemy-2.0.30-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:bc0c53579650a891f9b83fa3cecd4e00218e071d0ba00c4890f5be0c34887ed3"},
{file = "SQLAlchemy-2.0.30-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:311710f9a2ee235f1403537b10c7687214bb1f2b9ebb52702c5aa4a77f0b3af7"},
{file = "SQLAlchemy-2.0.30-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:408f8b0e2c04677e9c93f40eef3ab22f550fecb3011b187f66a096395ff3d9fd"},
{file = "SQLAlchemy-2.0.30-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:37a4b4fb0dd4d2669070fb05b8b8824afd0af57587393015baee1cf9890242d9"},
{file = "SQLAlchemy-2.0.30-cp38-cp38-musllinux_1_1_aarch64.whl", hash = "sha256:a943d297126c9230719c27fcbbeab57ecd5d15b0bd6bfd26e91bfcfe64220621"},
{file = "SQLAlchemy-2.0.30-cp38-cp38-musllinux_1_1_x86_64.whl", hash = "sha256:0a089e218654e740a41388893e090d2e2c22c29028c9d1353feb38638820bbeb"},
{file = "SQLAlchemy-2.0.30-cp38-cp38-win32.whl", hash = "sha256:fa561138a64f949f3e889eb9ab8c58e1504ab351d6cf55259dc4c248eaa19da6"},
{file = "SQLAlchemy-2.0.30-cp38-cp38-win_amd64.whl", hash = "sha256:7d74336c65705b986d12a7e337ba27ab2b9d819993851b140efdf029248e818e"},
{file = "SQLAlchemy-2.0.30-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:ae8c62fe2480dd61c532ccafdbce9b29dacc126fe8be0d9a927ca3e699b9491a"},
{file = "SQLAlchemy-2.0.30-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:2383146973a15435e4717f94c7509982770e3e54974c71f76500a0136f22810b"},
{file = "SQLAlchemy-2.0.30-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:8409de825f2c3b62ab15788635ccaec0c881c3f12a8af2b12ae4910a0a9aeef6"},
{file = "SQLAlchemy-2.0.30-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:0094c5dc698a5f78d3d1539853e8ecec02516b62b8223c970c86d44e7a80f6c7"},
{file = "SQLAlchemy-2.0.30-cp39-cp39-musllinux_1_1_aarch64.whl", hash = "sha256:edc16a50f5e1b7a06a2dcc1f2205b0b961074c123ed17ebda726f376a5ab0953"},
{file = "SQLAlchemy-2.0.30-cp39-cp39-musllinux_1_1_x86_64.whl", hash = "sha256:f7703c2010355dd28f53deb644a05fc30f796bd8598b43f0ba678878780b6e4c"},
{file = "SQLAlchemy-2.0.30-cp39-cp39-win32.whl", hash = "sha256:1f9a727312ff6ad5248a4367358e2cf7e625e98b1028b1d7ab7b806b7d757513"},
{file = "SQLAlchemy-2.0.30-cp39-cp39-win_amd64.whl", hash = "sha256:a0ef36b28534f2a5771191be6edb44cc2673c7b2edf6deac6562400288664221"},
{file = "SQLAlchemy-2.0.30-py3-none-any.whl", hash = "sha256:7108d569d3990c71e26a42f60474b4c02c8586c4681af5fd67e51a044fdea86a"},
{file = "SQLAlchemy-2.0.30.tar.gz", hash = "sha256:2b1708916730f4830bc69d6f49d37f7698b5bd7530aca7f04f785f8849e95255"},
{file = "SQLAlchemy-2.0.31-cp310-cp310-macosx_10_9_x86_64.whl", hash = "sha256:f2a213c1b699d3f5768a7272de720387ae0122f1becf0901ed6eaa1abd1baf6c"},
{file = "SQLAlchemy-2.0.31-cp310-cp310-macosx_11_0_arm64.whl", hash = "sha256:9fea3d0884e82d1e33226935dac990b967bef21315cbcc894605db3441347443"},
{file = "SQLAlchemy-2.0.31-cp310-cp310-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:f3ad7f221d8a69d32d197e5968d798217a4feebe30144986af71ada8c548e9fa"},
{file = "SQLAlchemy-2.0.31-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:9f2bee229715b6366f86a95d497c347c22ddffa2c7c96143b59a2aa5cc9eebbc"},
{file = "SQLAlchemy-2.0.31-cp310-cp310-musllinux_1_2_aarch64.whl", hash = "sha256:cd5b94d4819c0c89280b7c6109c7b788a576084bf0a480ae17c227b0bc41e109"},
{file = "SQLAlchemy-2.0.31-cp310-cp310-musllinux_1_2_x86_64.whl", hash = "sha256:750900a471d39a7eeba57580b11983030517a1f512c2cb287d5ad0fcf3aebd58"},
{file = "SQLAlchemy-2.0.31-cp310-cp310-win32.whl", hash = "sha256:7bd112be780928c7f493c1a192cd8c5fc2a2a7b52b790bc5a84203fb4381c6be"},
{file = "SQLAlchemy-2.0.31-cp310-cp310-win_amd64.whl", hash = "sha256:5a48ac4d359f058474fadc2115f78a5cdac9988d4f99eae44917f36aa1476327"},
{file = "SQLAlchemy-2.0.31-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:f68470edd70c3ac3b6cd5c2a22a8daf18415203ca1b036aaeb9b0fb6f54e8298"},
{file = "SQLAlchemy-2.0.31-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:2e2c38c2a4c5c634fe6c3c58a789712719fa1bf9b9d6ff5ebfce9a9e5b89c1ca"},
{file = "SQLAlchemy-2.0.31-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:bd15026f77420eb2b324dcb93551ad9c5f22fab2c150c286ef1dc1160f110203"},
{file = "SQLAlchemy-2.0.31-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:2196208432deebdfe3b22185d46b08f00ac9d7b01284e168c212919891289396"},
{file = "SQLAlchemy-2.0.31-cp311-cp311-musllinux_1_2_aarch64.whl", hash = "sha256:352b2770097f41bff6029b280c0e03b217c2dcaddc40726f8f53ed58d8a85da4"},
{file = "SQLAlchemy-2.0.31-cp311-cp311-musllinux_1_2_x86_64.whl", hash = "sha256:56d51ae825d20d604583f82c9527d285e9e6d14f9a5516463d9705dab20c3740"},
{file = "SQLAlchemy-2.0.31-cp311-cp311-win32.whl", hash = "sha256:6e2622844551945db81c26a02f27d94145b561f9d4b0c39ce7bfd2fda5776dac"},
{file = "SQLAlchemy-2.0.31-cp311-cp311-win_amd64.whl", hash = "sha256:ccaf1b0c90435b6e430f5dd30a5aede4764942a695552eb3a4ab74ed63c5b8d3"},
{file = "SQLAlchemy-2.0.31-cp312-cp312-macosx_10_9_x86_64.whl", hash = "sha256:3b74570d99126992d4b0f91fb87c586a574a5872651185de8297c6f90055ae42"},
{file = "SQLAlchemy-2.0.31-cp312-cp312-macosx_11_0_arm64.whl", hash = "sha256:6f77c4f042ad493cb8595e2f503c7a4fe44cd7bd59c7582fd6d78d7e7b8ec52c"},
{file = "SQLAlchemy-2.0.31-cp312-cp312-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:cd1591329333daf94467e699e11015d9c944f44c94d2091f4ac493ced0119449"},
{file = "SQLAlchemy-2.0.31-cp312-cp312-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:74afabeeff415e35525bf7a4ecdab015f00e06456166a2eba7590e49f8db940e"},
{file = "SQLAlchemy-2.0.31-cp312-cp312-musllinux_1_2_aarch64.whl", hash = "sha256:b9c01990d9015df2c6f818aa8f4297d42ee71c9502026bb074e713d496e26b67"},
{file = "SQLAlchemy-2.0.31-cp312-cp312-musllinux_1_2_x86_64.whl", hash = "sha256:66f63278db425838b3c2b1c596654b31939427016ba030e951b292e32b99553e"},
{file = "SQLAlchemy-2.0.31-cp312-cp312-win32.whl", hash = "sha256:0b0f658414ee4e4b8cbcd4a9bb0fd743c5eeb81fc858ca517217a8013d282c96"},
{file = "SQLAlchemy-2.0.31-cp312-cp312-win_amd64.whl", hash = "sha256:fa4b1af3e619b5b0b435e333f3967612db06351217c58bfb50cee5f003db2a5a"},
{file = "SQLAlchemy-2.0.31-cp37-cp37m-macosx_10_9_x86_64.whl", hash = "sha256:f43e93057cf52a227eda401251c72b6fbe4756f35fa6bfebb5d73b86881e59b0"},
{file = "SQLAlchemy-2.0.31-cp37-cp37m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:d337bf94052856d1b330d5fcad44582a30c532a2463776e1651bd3294ee7e58b"},
{file = "SQLAlchemy-2.0.31-cp37-cp37m-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c06fb43a51ccdff3b4006aafee9fcf15f63f23c580675f7734245ceb6b6a9e05"},
{file = "SQLAlchemy-2.0.31-cp37-cp37m-musllinux_1_2_aarch64.whl", hash = "sha256:b6e22630e89f0e8c12332b2b4c282cb01cf4da0d26795b7eae16702a608e7ca1"},
{file = "SQLAlchemy-2.0.31-cp37-cp37m-musllinux_1_2_x86_64.whl", hash = "sha256:79a40771363c5e9f3a77f0e28b3302801db08040928146e6808b5b7a40749c88"},
{file = "SQLAlchemy-2.0.31-cp37-cp37m-win32.whl", hash = "sha256:501ff052229cb79dd4c49c402f6cb03b5a40ae4771efc8bb2bfac9f6c3d3508f"},
{file = "SQLAlchemy-2.0.31-cp37-cp37m-win_amd64.whl", hash = "sha256:597fec37c382a5442ffd471f66ce12d07d91b281fd474289356b1a0041bdf31d"},
{file = "SQLAlchemy-2.0.31-cp38-cp38-macosx_10_9_x86_64.whl", hash = "sha256:dc6d69f8829712a4fd799d2ac8d79bdeff651c2301b081fd5d3fe697bd5b4ab9"},
{file = "SQLAlchemy-2.0.31-cp38-cp38-macosx_11_0_arm64.whl", hash = "sha256:23b9fbb2f5dd9e630db70fbe47d963c7779e9c81830869bd7d137c2dc1ad05fb"},
{file = "SQLAlchemy-2.0.31-cp38-cp38-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2a21c97efcbb9f255d5c12a96ae14da873233597dfd00a3a0c4ce5b3e5e79704"},
{file = "SQLAlchemy-2.0.31-cp38-cp38-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:26a6a9837589c42b16693cf7bf836f5d42218f44d198f9343dd71d3164ceeeac"},
{file = "SQLAlchemy-2.0.31-cp38-cp38-musllinux_1_2_aarch64.whl", hash = "sha256:dc251477eae03c20fae8db9c1c23ea2ebc47331bcd73927cdcaecd02af98d3c3"},
{file = "SQLAlchemy-2.0.31-cp38-cp38-musllinux_1_2_x86_64.whl", hash = "sha256:2fd17e3bb8058359fa61248c52c7b09a97cf3c820e54207a50af529876451808"},
{file = "SQLAlchemy-2.0.31-cp38-cp38-win32.whl", hash = "sha256:c76c81c52e1e08f12f4b6a07af2b96b9b15ea67ccdd40ae17019f1c373faa227"},
{file = "SQLAlchemy-2.0.31-cp38-cp38-win_amd64.whl", hash = "sha256:4b600e9a212ed59355813becbcf282cfda5c93678e15c25a0ef896b354423238"},
{file = "SQLAlchemy-2.0.31-cp39-cp39-macosx_10_9_x86_64.whl", hash = "sha256:5b6cf796d9fcc9b37011d3f9936189b3c8074a02a4ed0c0fbbc126772c31a6d4"},
{file = "SQLAlchemy-2.0.31-cp39-cp39-macosx_11_0_arm64.whl", hash = "sha256:78fe11dbe37d92667c2c6e74379f75746dc947ee505555a0197cfba9a6d4f1a4"},
{file = "SQLAlchemy-2.0.31-cp39-cp39-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:2fc47dc6185a83c8100b37acda27658fe4dbd33b7d5e7324111f6521008ab4fe"},
{file = "SQLAlchemy-2.0.31-cp39-cp39-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:8a41514c1a779e2aa9a19f67aaadeb5cbddf0b2b508843fcd7bafdf4c6864005"},
{file = "SQLAlchemy-2.0.31-cp39-cp39-musllinux_1_2_aarch64.whl", hash = "sha256:afb6dde6c11ea4525318e279cd93c8734b795ac8bb5dda0eedd9ebaca7fa23f1"},
{file = "SQLAlchemy-2.0.31-cp39-cp39-musllinux_1_2_x86_64.whl", hash = "sha256:3f9faef422cfbb8fd53716cd14ba95e2ef655400235c3dfad1b5f467ba179c8c"},
{file = "SQLAlchemy-2.0.31-cp39-cp39-win32.whl", hash = "sha256:fc6b14e8602f59c6ba893980bea96571dd0ed83d8ebb9c4479d9ed5425d562e9"},
{file = "SQLAlchemy-2.0.31-cp39-cp39-win_amd64.whl", hash = "sha256:3cb8a66b167b033ec72c3812ffc8441d4e9f5f78f5e31e54dcd4c90a4ca5bebc"},
{file = "SQLAlchemy-2.0.31-py3-none-any.whl", hash = "sha256:69f3e3c08867a8e4856e92d7afb618b95cdee18e0bc1647b77599722c9a28911"},
{file = "SQLAlchemy-2.0.31.tar.gz", hash = "sha256:b607489dd4a54de56984a0c7656247504bd5523d9d0ba799aef59d4add009484"},
]
[package.dependencies]
greenlet = {version = "!=0.4.17", markers = "platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\""}
greenlet = {version = "!=0.4.17", markers = "python_version < \"3.13\" and (platform_machine == \"aarch64\" or platform_machine == \"ppc64le\" or platform_machine == \"x86_64\" or platform_machine == \"amd64\" or platform_machine == \"AMD64\" or platform_machine == \"win32\" or platform_machine == \"WIN32\")"}
typing-extensions = ">=4.6.0"
[package.extras]
@@ -3846,13 +3862,13 @@ snowflake = ["snowflake-connector-python (>=2.8.0)", "snowflake-snowpark-python
[[package]]
name = "tenacity"
version = "8.3.0"
version = "8.4.1"
description = "Retry code until it succeeds"
optional = false
python-versions = ">=3.8"
files = [
{file = "tenacity-8.3.0-py3-none-any.whl", hash = "sha256:3649f6443dbc0d9b01b9d8020a9c4ec7a1ff5f6f3c6c8a036ef371f573fe9185"},
{file = "tenacity-8.3.0.tar.gz", hash = "sha256:953d4e6ad24357bceffbc9707bc74349aca9d245f68eb65419cf0c249a1949a2"},
{file = "tenacity-8.4.1-py3-none-any.whl", hash = "sha256:28522e692eda3e1b8f5e99c51464efcc0b9fc86933da92415168bc1c4e2308fa"},
{file = "tenacity-8.4.1.tar.gz", hash = "sha256:54b1412b878ddf7e1f1577cd49527bad8cdef32421bd599beac0c6c3f10582fd"},
]
[package.extras]
@@ -4012,13 +4028,13 @@ files = [
[[package]]
name = "urllib3"
version = "2.2.1"
version = "2.2.2"
description = "HTTP library with thread-safe connection pooling, file post, and more."
optional = false
python-versions = ">=3.8"
files = [
{file = "urllib3-2.2.1-py3-none-any.whl", hash = "sha256:450b20ec296a467077128bff42b73080516e71b56ff59a60a02bef2232c4fa9d"},
{file = "urllib3-2.2.1.tar.gz", hash = "sha256:d0570876c61ab9e520d776c38acbbb5b05a776d3f9ff98a5c8fd5162a444cf19"},
{file = "urllib3-2.2.2-py3-none-any.whl", hash = "sha256:a448b2f64d686155468037e1ace9f2d2199776e17f0a46610480d311f73e3472"},
{file = "urllib3-2.2.2.tar.gz", hash = "sha256:dd505485549a7a552833da5e6063639d0d177c04f23bc3864e41e5dc5f612168"},
]
[package.extras]
@@ -4636,4 +4652,4 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"]
[metadata]
lock-version = "2.0"
python-versions = "==3.12.0"
content-hash = "db6214bf0c8af641ee4b6a66c5e9c44f20dab8578b6686c68a46f6bd3ce4f979"
content-hash = "77cf58d792c26a1fb35a19ad82e5b390f30bbaa150832100518189c5207f059b"
+1
View File
@@ -22,6 +22,7 @@ sqlglot = "==22.5.0"
orjson = "==3.10.3"
sf-hamilton = {version = "==1.63.0", extras = ["visualization"]}
aiohttp = "==3.9.5"
ollama-haystack = "==0.0.6"
[tool.poetry.group.dev.dependencies]
pytest = "==8.2.0"
@@ -45,7 +45,7 @@ class GenerationPostProcessor:
"invalid_generation_results": invalid_generation_results,
}
except Exception as e:
logger.error(f"Error in GenerationPostProcessor: {e}")
logger.exception(f"Error in GenerationPostProcessor: {e}")
return {
"valid_generation_results": [],
@@ -42,7 +42,7 @@ class QueryUnderstandingPostProcessor:
)
def run(self, replies: List[str]):
try:
result = orjson.loads(replies[0])["result"]
result = orjson.loads(replies[0])["result"].lower()
if result == "yes":
return {
@@ -17,8 +17,7 @@ from haystack_integrations.document_stores.qdrant.filters import (
from qdrant_client.http import models as rest
from src.core.provider import DocumentStoreProvider
from src.providers.llm.openai import EMBEDDING_MODEL_DIMENSION
from src.providers.loader import provider
from src.providers.loader import get_default_embedding_model_dim, provider
class AsyncQdrantDocumentStore(QdrantDocumentStore):
@@ -199,7 +198,12 @@ class QdrantProvider(DocumentStoreProvider):
def get_store(
self,
embedding_model_dim: int = EMBEDDING_MODEL_DIMENSION,
embedding_model_dim: int = (
int(os.getenv("EMBEDDING_MODEL_DIMENSION"))
if os.getenv("EMBEDDING_MODEL_DIMENSION")
else 0
)
or get_default_embedding_model_dim(os.getenv("LLM_PROVIDER", "opeani")),
dataset_name: Optional[str] = None,
recreate_index: bool = False,
):
@@ -21,7 +21,7 @@ from src.providers.loader import provider
EMBEDDING_MODEL_NAME = "text-embedding-3-small"
EMBEDDING_MODEL_DIMENSION = 1536
logger = logging.getLogger("azure-openai")
logger = logging.getLogger("wren-ai-service")
AZURE_GENERATION_MODEL = "gpt-4-turbo"
AZURE_GENERATION_MODEL_KWARGS = {
"temperature": 0,
@@ -299,16 +299,25 @@ class AzureOpenAILLMProvider(LLMProvider):
embed_api_version: str = os.getenv("AZURE_EMBED_VERSION"),
generation_model: str = os.getenv("AZURE_GENERATION_MODEL")
or AZURE_GENERATION_MODEL,
embedding_model: str = os.getenv("EMBEDDING_MODEL") or EMBEDDING_MODEL_NAME,
embedding_model_dim: int = (
int(os.getenv("EMBEDDING_MODEL_DIMENSION"))
if os.getenv("EMBEDDING_MODEL_DIMENSION")
else 0
)
or EMBEDDING_MODEL_DIMENSION,
):
logger.info(f"Using Azure OpenAI Generation Model: {generation_model}")
self.chat_api_key = chat_api_key
self.chat_api_base = chat_api_base
self.chat_api_version = chat_api_version
self.embed_api_base = embed_api_base
self.embed_api_key = embed_api_key
self.embed_api_version = embed_api_version
self.generation_model = generation_model
self._generation_api_key = chat_api_key
self._generation_api_base = chat_api_base
self._generation_api_version = chat_api_version
self._generation_model = generation_model
self._embedding_api_base = embed_api_base
self._embedding_api_key = embed_api_key
self._embedding_api_version = embed_api_version
self._embedding_model = embedding_model
self._embedding_model_dim = embedding_model_dim
def get_generator(
self,
@@ -316,36 +325,28 @@ class AzureOpenAILLMProvider(LLMProvider):
system_prompt: Optional[str] = None,
):
return AsyncAzureGenerator(
api_key=self.chat_api_key,
model=self.generation_model,
api_base=self.chat_api_base,
api_version=self.chat_api_version,
api_key=self._generation_api_key,
model=self._generation_model,
api_base=self._generation_api_base,
api_version=self._generation_api_version,
system_prompt=system_prompt,
generation_kwargs=model_kwargs,
)
def get_text_embedder(
self,
model_name: str = EMBEDDING_MODEL_NAME,
model_dim: int = EMBEDDING_MODEL_DIMENSION,
):
def get_text_embedder(self):
return AsyncAzureTextEmbedder(
api_key=self.embed_api_key,
model=model_name,
dimensions=model_dim,
api_base_url=self.embed_api_base,
api_version=self.embed_api_version,
api_key=self._embedding_api_key,
model=self._embedding_model,
dimensions=self._embedding_model_dim,
api_base_url=self._embedding_api_base,
api_version=self._embedding_api_version,
)
def get_document_embedder(
self,
model_name: str = EMBEDDING_MODEL_NAME,
model_dim: int = EMBEDDING_MODEL_DIMENSION,
):
def get_document_embedder(self):
return AsyncAzureDocumentEmbedder(
api_key=self.embed_api_key,
model=model_name,
dimensions=model_dim,
api_base_url=self.embed_api_base,
api_version=self.embed_api_version,
api_key=self._embedding_api_key,
model=self._embedding_model,
dimensions=self._embedding_model_dim,
api_base_url=self._embedding_api_base,
api_version=self._embedding_api_version,
)
+307
View File
@@ -0,0 +1,307 @@
import logging
import os
import time
from typing import Any, Callable, Dict, List, Optional
import aiohttp
from haystack import Document, component
from haystack.dataclasses import StreamingChunk
from haystack_integrations.components.embedders.ollama import (
OllamaDocumentEmbedder,
OllamaTextEmbedder,
)
from haystack_integrations.components.generators.ollama import OllamaGenerator
from tqdm import tqdm
from src.core.provider import LLMProvider
from src.providers.loader import provider
logger = logging.getLogger("wren-ai-service")
OLLAMA_URL = "http://localhost:11434"
GENERATION_MODEL_NAME = "llama3:8b"
GENERATION_MODEL_KWARGS = {
"temperature": 0,
}
EMBEDDING_MODEL_NAME = "nomic-embed-text"
EMBEDDING_MODEL_DIMENSION = 768 # https://huggingface.co/nomic-ai/nomic-embed-text-v1.5
@component
class AsyncGenerator(OllamaGenerator):
def __init__(
self,
model: str = "orca-mini",
url: str = "http://localhost:11434/api/generate",
generation_kwargs: Optional[Dict[str, Any]] = None,
system_prompt: Optional[str] = None,
template: Optional[str] = None,
raw: bool = False,
timeout: int = 120,
streaming_callback: Optional[Callable[[StreamingChunk], None]] = None,
):
super(AsyncGenerator, self).__init__(
model=model,
url=url,
generation_kwargs=generation_kwargs,
system_prompt=system_prompt,
template=template,
raw=raw,
timeout=timeout,
streaming_callback=streaming_callback,
)
async def _handle_streaming_response(self, response) -> List[StreamingChunk]:
"""
Handles Streaming response cases
"""
chunks: List[StreamingChunk] = []
for chunk in await response.iter_lines():
chunk_delta: StreamingChunk = self._build_chunk(chunk)
chunks.append(chunk_delta)
if self.streaming_callback is not None:
self.streaming_callback(chunk_delta)
return chunks
async def _convert_to_response(
self, ollama_response: aiohttp.ClientResponse
) -> Dict[str, List[Any]]:
"""
Converts a response from the Ollama API to the required Haystack format.
"""
resp_dict = await ollama_response.json()
replies = [resp_dict["response"]]
meta = {key: value for key, value in resp_dict.items() if key != "response"}
return {"replies": replies, "meta": [meta]}
def _create_json_payload(
self, prompt: str, stream: bool, generation_kwargs=None
) -> Dict[str, Any]:
"""
Returns a dictionary of JSON arguments for a POST request to an Ollama service.
"""
generation_kwargs = generation_kwargs or {}
return {
"prompt": prompt,
"model": self.model,
"stream": stream,
"raw": self.raw,
"format": "json", # https://github.com/ollama/ollama/blob/main/docs/api.md#request-json-mode
"template": self.template,
"system": self.system_prompt,
"options": generation_kwargs,
}
@component.output_types(replies=List[str], meta=List[Dict[str, Any]])
async def run(
self,
prompt: str,
generation_kwargs: Optional[Dict[str, Any]] = None,
):
logger.debug(f"Running Ollama generator with prompt: {prompt}")
generation_kwargs = {**self.generation_kwargs, **(generation_kwargs or {})}
stream = self.streaming_callback is not None
json_payload = self._create_json_payload(prompt, stream, generation_kwargs)
async with aiohttp.ClientSession(
timeout=aiohttp.ClientTimeout(self.timeout)
) as session:
response = await session.post(
self.url,
json=json_payload,
)
if stream:
chunks: List[StreamingChunk] = await self._handle_streaming_response(
response
)
return self._convert_to_streaming_response(chunks)
return await self._convert_to_response(response)
@component
class AsyncTextEmbedder(OllamaTextEmbedder):
def __init__(
self,
model: str = "nomic-embed-text",
url: str = "http://localhost:11434/api/embeddings",
generation_kwargs: Optional[Dict[str, Any]] = None,
timeout: int = 120,
):
super(AsyncTextEmbedder, self).__init__(
model=model,
url=url,
generation_kwargs=generation_kwargs,
timeout=timeout,
)
@component.output_types(embedding=List[float], meta=Dict[str, Any])
async def run(
self,
text: str,
generation_kwargs: Optional[Dict[str, Any]] = None,
):
logger.debug(f"Running Ollama text embedder with text: {text}")
payload = self._create_json_payload(text, generation_kwargs)
start = time.perf_counter()
async with aiohttp.ClientSession(
timeout=aiohttp.ClientTimeout(self.timeout)
) as session:
async with session.post(
self.url,
json=payload,
) as response:
elapsed = time.perf_counter() - start
result = await response.json()
result["meta"] = {"model": self.model, "duration": elapsed}
return result
@component
class AsyncDocumentEmbedder(OllamaDocumentEmbedder):
def __init__(
self,
model: str = "nomic-embed-text",
url: str = "http://localhost:11434/api/embeddings",
generation_kwargs: Optional[Dict[str, Any]] = None,
timeout: int = 120,
prefix: str = "",
suffix: str = "",
progress_bar: bool = True,
meta_fields_to_embed: Optional[List[str]] = None,
embedding_separator: str = "\n",
):
super(AsyncDocumentEmbedder, self).__init__(
model=model,
url=url,
generation_kwargs=generation_kwargs,
timeout=timeout,
prefix=prefix,
suffix=suffix,
progress_bar=progress_bar,
meta_fields_to_embed=meta_fields_to_embed,
embedding_separator=embedding_separator,
)
async def _embed_batch(
self,
texts_to_embed: List[str],
batch_size: int,
generation_kwargs: Optional[Dict[str, Any]] = None,
):
"""
Ollama Embedding only allows single uploads, not batching. Currently the batch size is set to 1.
If this changes in the future, line 86 (the first line within the for loop), can contain:
batch = texts_to_embed[i + i + batch_size]
"""
all_embeddings = []
meta: Dict[str, Any] = {"model": self.model}
async with aiohttp.ClientSession(
timeout=aiohttp.ClientTimeout(self.timeout)
) as session:
for i in tqdm(
range(0, len(texts_to_embed), batch_size),
disable=not self.progress_bar,
desc="Calculating embeddings",
):
batch = texts_to_embed[i] # Single batch only
payload = self._create_json_payload(batch, generation_kwargs)
async with session.post(
self.url,
json=payload,
) as response:
result = await response.json()
all_embeddings.append(result["embedding"])
return all_embeddings, meta
@component.output_types(embedding=List[float], meta=Dict[str, Any])
async def run(
self,
documents: List[str],
generation_kwargs: Optional[Dict[str, Any]] = None,
):
logger.debug(f"Running Ollama document embedder with documents: {documents}")
if (
not isinstance(documents, list)
or documents
and not isinstance(documents[0], Document)
):
msg = (
"OllamaDocumentEmbedder expects a list of Documents as input."
"In case you want to embed a list of strings, please use the OllamaTextEmbedder."
)
raise TypeError(msg)
texts_to_embed = self._prepare_texts_to_embed(documents=documents)
embeddings, meta = await self._embed_batch(
texts_to_embed=texts_to_embed,
batch_size=self.batch_size,
generation_kwargs=generation_kwargs,
)
for doc, emb in zip(documents, embeddings):
doc.embedding = emb
return {"documents": documents, "meta": meta}
@provider("ollama")
class OllamaLLMProvider(LLMProvider):
def __init__(
self,
url: str = os.getenv("OLLAMA_URL") or OLLAMA_URL,
generation_model: str = os.getenv("GENERATION_MODEL") or GENERATION_MODEL_NAME,
embedding_model: str = os.getenv("EMBEDDING_MODEL") or EMBEDDING_MODEL_NAME,
):
logger.info(f"Using Ollama Generation Model: {generation_model}")
self._url = url
self._generation_model = generation_model
self._embedding_model = embedding_model
def get_generator(
self,
model_kwargs: Optional[Dict[str, Any]] = GENERATION_MODEL_KWARGS,
system_prompt: Optional[str] = None,
):
return AsyncGenerator(
model=self._generation_model,
url=f"{self._url}/api/generate",
generation_kwargs=model_kwargs,
system_prompt=system_prompt,
)
def get_text_embedder(
self,
model_kwargs: Optional[Dict[str, Any]] = None,
):
return AsyncTextEmbedder(
model=self._embedding_model,
url=f"{self._url}/api/embeddings",
generation_kwargs=model_kwargs,
)
def get_document_embedder(
self,
model_kwargs: Optional[Dict[str, Any]] = None,
):
return AsyncDocumentEmbedder(
model=self._embedding_model,
url=f"{self._url}/api/embeddings",
generation_kwargs=model_kwargs,
)
+26 -29
View File
@@ -276,8 +276,14 @@ class OpenAILLMProvider(LLMProvider):
self,
api_key: Secret = Secret.from_env_var("OPENAI_API_KEY"),
api_base: str = os.getenv("OPENAI_API_BASE") or OPENAI_API_BASE,
generation_model: str = os.getenv("OPENAI_GENERATION_MODEL")
or GENERATION_MODEL_NAME,
embedding_model: str = os.getenv("EMBEDDING_MODEL") or EMBEDDING_MODEL_NAME,
embedding_model_dim: int = (
int(os.getenv("EMBEDDING_MODEL_DIMENSION"))
if os.getenv("EMBEDDING_MODEL_DIMENSION")
else 0
)
or EMBEDDING_MODEL_DIMENSION,
generation_model: str = os.getenv("GENERATION_MODEL") or GENERATION_MODEL_NAME,
):
def _verify_api_key(api_key: str, api_base: str) -> None:
"""
@@ -285,54 +291,45 @@ class OpenAILLMProvider(LLMProvider):
"""
OpenAI(api_key=api_key, base_url=api_base).models.list()
_verify_api_key(api_key.resolve_value(), api_base)
logger.info(f"Using OpenAI Generation Model: {generation_model}")
# TODO: currently only OpenAI api key can be verified
if api_base == OPENAI_API_BASE:
_verify_api_key(api_key.resolve_value(), api_base)
logger.info(f"Using OpenAI Generation Model: {generation_model}")
else:
logger.info(
f"Using OpenAI API-compatible Generation Model: {generation_model}"
)
self._api_key = api_key
self._api_base = api_base
self._embedding_model = embedding_model
self._embedding_model_dim = embedding_model_dim
self._generation_model = generation_model
def get_generator(
self,
model_kwargs: Optional[Dict[str, Any]] = None,
model_kwargs: Optional[Dict[str, Any]] = GENERATION_MODEL_KWARGS,
system_prompt: Optional[str] = None,
):
def _get_generation_kwargs(
api_base: str,
model_kwargs: Optional[Dict[str, Any]] = None,
):
if api_base == OPENAI_API_BASE:
return GENERATION_MODEL_KWARGS
return model_kwargs
return AsyncGenerator(
api_key=self._api_key,
api_base_url=self._api_base,
model=self._generation_model,
system_prompt=system_prompt,
generation_kwargs=_get_generation_kwargs(self._api_base, model_kwargs),
generation_kwargs=model_kwargs,
)
def get_text_embedder(
self,
model_name: str = EMBEDDING_MODEL_NAME,
model_dim: int = EMBEDDING_MODEL_DIMENSION,
):
def get_text_embedder(self):
return AsyncTextEmbedder(
api_key=self._api_key,
api_base_url=self._api_base,
model=model_name,
dimensions=model_dim,
model=self._embedding_model,
dimensions=self._embedding_model_dim,
)
def get_document_embedder(
self,
model_name: str = EMBEDDING_MODEL_NAME,
model_dim: int = EMBEDDING_MODEL_DIMENSION,
):
def get_document_embedder(self):
return AsyncDocumentEmbedder(
api_key=self._api_key,
api_base_url=self._api_base,
model=model_name,
dimensions=model_dim,
model=self._embedding_model,
dimensions=self._embedding_model_dim,
)
+6
View File
@@ -91,3 +91,9 @@ def get_provider(name: str):
logger.debug(f"Getting provider: {name} from {PROVIDERS}")
return PROVIDERS[name]
def get_default_embedding_model_dim(llm_provider: str):
return importlib.import_module(
f"src.providers.llm.{llm_provider}"
).EMBEDDING_MODEL_DIMENSION
+3 -2
View File
@@ -127,7 +127,7 @@ class AskService:
status="finished",
)
except Exception as e:
logger.error(f"ask pipeline - Failed to prepare semantics: {e}")
logger.exception(f"ask pipeline - Failed to prepare semantics: {e}")
self._prepare_semantics_statuses[
prepare_semantics_request.id
@@ -343,7 +343,8 @@ class AskService:
response=results,
)
except Exception as e:
logger.error(f"ask pipeline - OTHERS: {e}")
logger.exception(f"ask pipeline - OTHERS: {e}")
self._ask_results[query_id] = AskResultResponse(
status="failed",
error=AskResultResponse.AskError(
@@ -106,7 +106,8 @@ class AskDetailsService:
),
)
except Exception as e:
logger.error(f"ask-details pipeline - OTHERS: {e}")
logger.exception(f"ask-details pipeline - OTHERS: {e}")
self._ask_details_results[query_id] = AskDetailsResultResponse(
status="failed",
error=AskDetailsResultResponse.AskDetailsError(
@@ -26,7 +26,8 @@ with open(f"./outputs/locust/{filename}.json", "r") as f:
formatted = {
"llm provider": os.getenv("LLM_PROVIDER"),
"generation model": os.getenv("OPENAI_GENERATION_MODEL"),
"generation model": os.getenv("GENERATION_MODEL"),
"embedding model": os.getenv("EMBEDDING_MODEL"),
"locustfile": "tests/locust/locustfile.py",
"test results": test_results,
}
@@ -3,21 +3,27 @@ from src.providers import loader
def test_import_mods():
loader.import_mods("src.providers")
assert len(loader.PROVIDERS) == 5
assert len(loader.PROVIDERS) == 6
def test_get_provider():
loader.import_mods("src.providers")
# llm provider
provider = loader.get_provider("openai")
assert provider.__name__ == "OpenAILLMProvider"
provider = loader.get_provider("qdrant")
assert provider.__name__ == "QdrantProvider"
provider = loader.get_provider("azure_openai")
assert provider.__name__ == "AzureOpenAILLMProvider"
provider = loader.get_provider("ollama")
assert provider.__name__ == "OllamaLLMProvider"
# document store provider
provider = loader.get_provider("qdrant")
assert provider.__name__ == "QdrantProvider"
# engine provider
provider = loader.get_provider("wren-ui")
assert provider.__name__ == "WrenUI"
@@ -52,7 +52,7 @@ services:
WREN_ENGINE_ENDPOINT: http://engine:${WREN_ENGINE_PORT}
WREN_AI_ENDPOINT: http://host.docker.internal:${WREN_AI_SERVICE_PORT}
IBIS_SERVER_ENDPOINT: http://ibis:${IBIS_SERVER_PORT}
OPENAI_GENERATION_MODEL: ${OPENAI_GENERATION_MODEL}
GENERATION_MODEL: ${GENERATION_MODEL}
# telemetry
WREN_ENGINE_PORT: ${WREN_ENGINE_PORT}
WREN_AI_SERVICE_VERSION: ${WREN_AI_SERVICE_VERSION}
+57 -11
View File
@@ -43,6 +43,26 @@ func evaluateTelemetryPreferences() (bool, error) {
return true, nil
}
func askForLLMProvider() (string, error) {
// let users know we're asking for a LLM provider
fmt.Println("Please provide the LLM provider you want to use")
fmt.Println("You can learn more about how to set up custom LLMs at https://docs.getwren.ai/installation/custom_llm#running-wren-ai-with-your-custom-llm-or-document-store")
prompt := promptui.Select{
Label: "Select an LLM provider",
Items: []string{"OpenAI", "Custom"},
}
_, result, err := prompt.Run()
if err != nil {
fmt.Printf("Prompt failed %v\n", err)
return "", err
}
return result, nil
}
func askForAPIKey() (string, error) {
// let users know we're asking for an API key
fmt.Println("Please provide your OpenAI API key")
@@ -93,6 +113,19 @@ func askForGenerationModel() (string, error) {
return result, nil
}
func isEnvFileValidForCustomLLM(projectDir string) error {
// validate if .env.ai file exists in ~/.wrenai
envFilePath := path.Join(projectDir, ".env.ai")
if _, err := os.Stat(envFilePath); os.IsNotExist(err) {
errMessage := fmt.Sprintf("Please create a .env.ai file in %s first, more details at https://docs.getwren.ai/installation/custom_llm#running-wren-ai-with-your-custom-llm-or-document-store", projectDir)
return errors.New(errMessage)
}
return nil
}
func Launch() {
// recover from panic
defer func() {
@@ -108,13 +141,30 @@ func Launch() {
myFigure.Print()
fmt.Println(strings.Repeat("=", 55))
// ask for OpenAI API key
pterm.Print("\n")
apiKey, err := askForAPIKey()
// prepare a project directory
pterm.Info.Println("Preparing project directory")
projectDir := prepareProjectDir()
// ask for OpenAI generation model
// ask for LLM provider
pterm.Print("\n")
generationModel, err := askForGenerationModel()
llmProvider, err := askForLLMProvider()
openaiApiKey := ""
openaiGenerationModel := ""
if llmProvider == "OpenAI" {
// ask for OpenAI API key
pterm.Print("\n")
openaiApiKey, _ = askForAPIKey()
// ask for OpenAI generation model
pterm.Print("\n")
openaiGenerationModel, _ = askForGenerationModel()
} else {
// check if .env.ai file exists
err = isEnvFileValidForCustomLLM(projectDir)
if err != nil {
panic(err)
}
}
// ask for telemetry consent
pterm.Print("\n")
@@ -142,17 +192,13 @@ func Launch() {
time.Sleep(5 * time.Second)
}
// prepare a project directory
pterm.Info.Println("Preparing project directory")
projectDir := prepareProjectDir()
// download docker-compose file and env file template for Wren AI
pterm.Info.Println("Downloading docker-compose file and env file")
// find an available port
uiPort := utils.FindAvailablePort(3000)
aiPort := utils.FindAvailablePort(5555)
err = utils.PrepareDockerFiles(apiKey, generationModel, uiPort, aiPort, projectDir, telemetryEnabled)
err = utils.PrepareDockerFiles(openaiApiKey, openaiGenerationModel, uiPort, aiPort, projectDir, telemetryEnabled)
if err != nil {
panic(err)
}
@@ -160,7 +206,7 @@ func Launch() {
// launch Wren AI
pterm.Info.Println("Launching Wren AI")
const projectName string = "wrenai"
err = utils.RunDockerCompose(projectName, projectDir)
err = utils.RunDockerCompose(projectName, projectDir, llmProvider)
if err != nil {
panic(err)
}
+24 -8
View File
@@ -32,14 +32,14 @@ const (
PG_USERNAME string = "wren-user"
)
func replaceEnvFileContent(content string, OpenaiApiKey string, OpenaiGenerationModel string, hostPort int, aiPort int, pg_password string, userUUID string, telemetryEnabled bool) string {
func replaceEnvFileContent(content string, openaiApiKey string, openAIGenerationModel string, hostPort int, aiPort int, pg_password string, userUUID string, telemetryEnabled bool) string {
// replace OPENAI_API_KEY
reg := regexp.MustCompile(`OPENAI_API_KEY=sk-(.*)`)
str := reg.ReplaceAllString(content, "OPENAI_API_KEY="+OpenaiApiKey)
str := reg.ReplaceAllString(content, "OPENAI_API_KEY="+openaiApiKey)
// replace OPENAI_GENERATION_MODEL
reg = regexp.MustCompile(`OPENAI_GENERATION_MODEL=(.*)`)
str = reg.ReplaceAllString(str, "OPENAI_GENERATION_MODEL="+OpenaiGenerationModel)
// replace GENERATION_MODEL
reg = regexp.MustCompile(`GENERATION_MODEL=(.*)`)
str = reg.ReplaceAllString(str, "GENERATION_MODEL="+openAIGenerationModel)
// replace USER_UUID
reg = regexp.MustCompile(`USER_UUID=(.*)`)
@@ -185,8 +185,18 @@ func PrepareDockerFiles(openaiApiKey string, openaiGenerationModel string, hostP
if err != nil {
return err
}
// replace the content with regex
envFileContent := replaceEnvFileContent(string(envExampleFileContent), openaiApiKey, openaiGenerationModel, hostPort, aiPort, pg_pwd, userUUID, telemetryEnabled)
envFileContent := replaceEnvFileContent(
string(envExampleFileContent),
openaiApiKey,
openaiGenerationModel,
hostPort,
aiPort,
pg_pwd,
userUUID,
telemetryEnabled,
)
newEnvFile := getEnvFilePath(projectDir)
// write the file
err = os.WriteFile(newEnvFile, []byte(envFileContent), 0644)
@@ -207,10 +217,16 @@ func getEnvFilePath(projectDir string) string {
return path.Join(projectDir, ".env")
}
func RunDockerCompose(projectName string, projectDir string) error {
func RunDockerCompose(projectName string, projectDir string, llmProvider string) error {
ctx := context.Background()
composeFilePath := path.Join(projectDir, "docker-compose.yaml")
envFile := path.Join(projectDir, ".env")
envFiles := []string{envFile}
if llmProvider == "Custom" {
customEnvFile := path.Join(projectDir, ".env.ai")
envFiles = append(envFiles, customEnvFile)
}
// docker-compose up
dockerCli, err := command.NewDockerCli()
@@ -237,7 +253,7 @@ func RunDockerCompose(projectName string, projectDir string) error {
ProjectName: projectName,
ConfigPaths: []string{composeFilePath},
WorkDir: projectDir,
EnvFiles: []string{envFile},
EnvFiles: envFiles,
}
// Turn projectOptions into a project with default values
+2 -2
View File
@@ -19,7 +19,7 @@ export interface IConfig {
// wren AI
wrenAIEndpoint: string;
openaiGenerationModel?: string;
generationModel?: string;
// ibis server
ibisServerEndpoint: string;
@@ -102,7 +102,7 @@ const config = {
// wren AI
wrenAIEndpoint: process.env.WREN_AI_ENDPOINT,
openaiGenerationModel: process.env.OPENAI_GENERATION_MODEL,
generationModel: process.env.GENERATION_MODEL,
// ibis server
ibisServerEndpoint: process.env.IBIS_SERVER_ENDPOINT,
@@ -12,7 +12,7 @@ const {
userUUID,
telemetryEnabled,
wrenAIVersion,
openaiGenerationModel,
generationModel,
wrenEngineVersion,
wrenUIVersion,
posthogApiKey,
@@ -67,7 +67,7 @@ export class Telemetry {
'wren-ai-service-version': wrenAIVersion || null,
// collect AI model info
'openai-generation-model': openaiGenerationModel || null,
'generation-model': generationModel || null,
// collect some system info from process module
node_version: process.version,