Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: introducing configurable retrieval workflows #3227

Merged
merged 99 commits into from
Sep 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
99 commits
Select commit Hold shift + click to select a range
faff989
add: added Pydantic class 'RerankerConfig' to manage the configuratio…
jacopo-chevallard Sep 10, 2024
b332cfd
feat: adding Cohere / Jina (multilingual) rerankers. To use reranking…
jacopo-chevallard Sep 10, 2024
ce71e3c
add: added QuivrBaseConfig class as the base class for all Quivr conf…
jacopo-chevallard Sep 11, 2024
3a0bb05
add: added configuration for MegaParse
jacopo-chevallard Sep 11, 2024
db443d8
feat: using QuivrBaseConfig as the base class, adding configurations …
jacopo-chevallard Sep 11, 2024
5cfe2c7
add: added example yaml file to configure our RAG pipeline (ingestion…
jacopo-chevallard Sep 11, 2024
74f67a4
add: adding default initializations to the different config classes
jacopo-chevallard Sep 11, 2024
47334c4
add: adding MegaParse as an internal dependency to simplify the devel…
jacopo-chevallard Sep 11, 2024
1111a27
chore: adding MegaParse as an internal package
jacopo-chevallard Sep 11, 2024
075f8c0
feat: adding configuration to Megaparse
jacopo-chevallard Sep 11, 2024
9547444
feat: adding logger, using MegaParseConfig to pass options to parsers
jacopo-chevallard Sep 11, 2024
3cb08a4
feat: using MegaParseConfig
jacopo-chevallard Sep 11, 2024
f9f1619
feat: checking that the correct API key is provided when configuring …
jacopo-chevallard Sep 11, 2024
adf7231
fix: correctly passing parsers (processor) kwargs
jacopo-chevallard Sep 11, 2024
35f19cb
chore: adding MegaParse as an internal dependency
jacopo-chevallard Sep 12, 2024
c332e81
feat: improving logging message
jacopo-chevallard Sep 12, 2024
09d3a31
feat: changing default RAG configuration
jacopo-chevallard Sep 12, 2024
d87a804
feat: adding example usage of yaml configuration
jacopo-chevallard Sep 12, 2024
5caae05
refactor: changed RAGConfig --> AssistantConfig
jacopo-chevallard Sep 12, 2024
2bafc1d
feat: renaming RAGConfig into AssistantConfig and moving the current …
jacopo-chevallard Sep 12, 2024
5b7b1b0
feat: using RetrievalConfig instead of AssistantConfig (which has rep…
jacopo-chevallard Sep 12, 2024
5f4087f
feat: update test to use RetrievalConfig
jacopo-chevallard Sep 12, 2024
3f67398
feat: using QuivrQARAGLangGraph instead of QuivrQARAG
jacopo-chevallard Sep 12, 2024
3a3afe7
refactor: renaming build_langgraph_chain to build_chain to ease trans…
jacopo-chevallard Sep 12, 2024
061c9c6
feat: testing QuivrQARAGLangGraph instead of QuivrQARAG; using Retrie…
jacopo-chevallard Sep 12, 2024
232b4c8
fix: making processor_kwargs an optional argument
jacopo-chevallard Sep 12, 2024
e0a2990
feat: using by default the QuivrQARAGLangGraph class instead of Quivr…
jacopo-chevallard Sep 12, 2024
dddfacd
refactor: in LLMEndpointConfig, renaming max_tokens --> max_output_to…
jacopo-chevallard Sep 12, 2024
8ee316b
style: adding arguments to simplify code understanding
jacopo-chevallard Sep 12, 2024
ca9ec90
feat: fixing ruff errors, using Rerankers settings from yaml configur…
jacopo-chevallard Sep 12, 2024
dee0d49
chore: adding types-pyyaml dependency and instructing mypy to use it
jacopo-chevallard Sep 12, 2024
aa3f6b3
feat: setting LLM supplier and model separetely
jacopo-chevallard Sep 12, 2024
52a9e47
feat: updated example yaml file to reflect updated configuration classes
jacopo-chevallard Sep 12, 2024
b8ac4be
feat: explicitly passing API to the rerankers constructors (otherwise…
jacopo-chevallard Sep 13, 2024
4e3a4a4
feat: adding tokenizer information to enable proper token counting fo…
jacopo-chevallard Sep 13, 2024
1fdad0c
feat: setting anv variable to avoid the warning 'huggingface/tokenize…
jacopo-chevallard Sep 13, 2024
e617045
feat: first implementation of a configurable retrieval workflow
jacopo-chevallard Sep 13, 2024
8f9f3a5
feat: using proper token counting
jacopo-chevallard Sep 13, 2024
cbd2127
chore: removed unused options
jacopo-chevallard Sep 13, 2024
c48d0b9
feat: updated test script
jacopo-chevallard Sep 13, 2024
f9a84cb
feat: prevent extra fields in the configuration classes, as a way to …
jacopo-chevallard Sep 13, 2024
92e6875
feat: adding example field for custom prompt
jacopo-chevallard Sep 13, 2024
5720b17
feat: using rich for better error / backtrace formatting
jacopo-chevallard Sep 13, 2024
16f7fbf
Merge branch 'main' into feat/workflows
jacopo-chevallard Sep 13, 2024
64d0d33
fix: fixing config variable name
jacopo-chevallard Sep 13, 2024
6b31e0b
fix: fixing CI tests
jacopo-chevallard Sep 13, 2024
7fef189
feat: using PdfParser from the megaparse.config module; fixing mypy e…
jacopo-chevallard Sep 16, 2024
fe6b7d4
feat: setting unstructured as the default parser
jacopo-chevallard Sep 16, 2024
2ca4f03
fix: fixing test fail because of arguments name mismatch
jacopo-chevallard Sep 16, 2024
5163375
fix: fixing failing test
jacopo-chevallard Sep 16, 2024
1dd93d0
feat: using the QuivrKnowledge BaseModel from quivr-core; fixing mypy…
jacopo-chevallard Sep 16, 2024
e64a63f
feat: extending the model defintion to cover the use of QuivrKnowledg…
jacopo-chevallard Sep 16, 2024
0b6cd5d
fix: fixing Docker dev image build to account for the use of MegaPars…
jacopo-chevallard Sep 16, 2024
e0969be
feat: making BrainEntity inherit from BrainConfig, fixing Pydnatic er…
jacopo-chevallard Sep 16, 2024
f932a5a
feat: introducing the BrainConfig Pydantic data class
jacopo-chevallard Sep 16, 2024
f8209f9
feat: first integration of quivr-core brain.Brain into quivr-api rag_…
jacopo-chevallard Sep 16, 2024
c7bd27a
feat: first working version of quivr-api wokring with an externral YA…
jacopo-chevallard Sep 16, 2024
bc8ea9d
feat: adding example of yaml configuration file for the retrieval pip…
jacopo-chevallard Sep 17, 2024
071780f
feat: updated example .env file
jacopo-chevallard Sep 17, 2024
51aeccc
feat: structuring our custom prompts into a dynamic Pydantic BaseClass
jacopo-chevallard Sep 17, 2024
0d32892
feat: enabling use of QuivrQARAGLangGraph to handle a workflow withou…
jacopo-chevallard Sep 17, 2024
37124e2
feat: making some fields optional to be able to use BrainEntity also …
jacopo-chevallard Sep 18, 2024
a05c0bb
feat: making the VectorStore and Embedder optional to deal with chat-…
jacopo-chevallard Sep 18, 2024
2103480
feat: using a configured RAGService instead of ChatLLMService to avoi…
jacopo-chevallard Sep 18, 2024
5a99c12
feat: added configuration file for chat-with-llm modality
jacopo-chevallard Sep 18, 2024
162dafe
feat: updated configuration files
jacopo-chevallard Sep 18, 2024
e3a1a87
docs: added important comment to the workflow configuration file
jacopo-chevallard Sep 18, 2024
7394e78
feat: removing ChatLLMService since we're now using RAGService for ch…
jacopo-chevallard Sep 18, 2024
d5de45f
feat: removing ChatLLM since we're now using QuivrQARAGLangGraph or c…
jacopo-chevallard Sep 18, 2024
6f8942b
feat: removing tests of ChatLLM
jacopo-chevallard Sep 18, 2024
d760b41
feat: removing supplier/model setting, since they are passed directly…
jacopo-chevallard Sep 18, 2024
f82892c
feat: enabling explicitly setting the model name after the RetrievalC…
jacopo-chevallard Sep 18, 2024
8ed078f
feat: removing ChatLLM
jacopo-chevallard Sep 18, 2024
8473f39
chore: adding sentencepiece dependency, necessary to run the tokenize…
jacopo-chevallard Sep 18, 2024
c9e9982
refactor: moved yaml file
jacopo-chevallard Sep 18, 2024
1291143
feat: updating list of supported models, enabling simpler merging of …
jacopo-chevallard Sep 18, 2024
779ff43
feat: loading the yaml configuration in quivr-api / chat_routes and m…
jacopo-chevallard Sep 18, 2024
2ff0786
feat: re-establishing the use of the LangChain ChatOpenAI interface f…
jacopo-chevallard Sep 18, 2024
b50a901
feat: updated example .env file
jacopo-chevallard Sep 18, 2024
8ac4987
feat: moving into a function the logic of loading the yaml configurat…
jacopo-chevallard Sep 18, 2024
6fb7bc7
test: fixing test errors
jacopo-chevallard Sep 18, 2024
1a0c984
test: fixing test errors
jacopo-chevallard Sep 18, 2024
d6b3387
Merge branch 'main' into feat/workflows
jacopo-chevallard Sep 19, 2024
ea46138
fix: fixing wrong match between model supplier (OpenAI/Anthropic) and…
jacopo-chevallard Sep 19, 2024
86cf639
refacto: DefaultLLMs --> DefaultModelSuppliers
jacopo-chevallard Sep 20, 2024
1cd0ff8
feat: forcing the start of a workflow with a START node
jacopo-chevallard Sep 20, 2024
2e5abbe
feat: removing redundant brain_id attribute
jacopo-chevallard Sep 20, 2024
4fc9437
fix: missing attribute
jacopo-chevallard Sep 20, 2024
cf8e793
feat: avoid re-creating LangGraph graph at each call
jacopo-chevallard Sep 20, 2024
ac46179
feat: correctly dealing with response.first() returning None
jacopo-chevallard Sep 20, 2024
f95dd0e
fix: fixing 'meaning' type
jacopo-chevallard Sep 20, 2024
581fcac
fix: fixing chat not saved in chat-with-llm mode
jacopo-chevallard Sep 20, 2024
059c4bf
feat: adding fallback tokenizer in the case HuggingFace tokenizers ca…
jacopo-chevallard Sep 23, 2024
08e85f9
refactor: moving yaml configuration files to config folder
jacopo-chevallard Sep 23, 2024
dc4e150
fix: reverting to BrainEntity.meaning as a string, since Supabase is …
jacopo-chevallard Sep 23, 2024
de10e83
fix: encapsulating into a function the logic of retrieving the path t…
jacopo-chevallard Sep 23, 2024
98b3b4f
feat: updated example .env file
jacopo-chevallard Sep 23, 2024
78b713d
feat: fallback to a default path for the yaml configuration files if …
jacopo-chevallard Sep 23, 2024
7791a12
fix: correctly returning the model metadata, used in chat-with-llm mode
jacopo-chevallard Sep 23, 2024
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
26 changes: 23 additions & 3 deletions .env.example
Original file line number Diff line number Diff line change
@@ -1,8 +1,30 @@
#### QUIVR Configuration
# This file is used to configure the Quivr stack. It is used by the `docker-compose.yml` file to configure the stack.

# API KEYS
# OPENAI. Update this to use your API key. To skip OpenAI integration use a fake key, for example: tk-aabbccddAABBCCDDEeFfGgHhIiJKLmnopjklMNOPqQqQqQqQ
OPENAI_API_KEY=CHANGE_ME
OPENAI_API_KEY=your-openai-api-key
# ANTHROPIC_API_KEY=your-anthropic-api-key
# MISTRAL_API_KEY=your-mistral-api-key
# GROQ_API_KEY=your-groq-api-key

COHERE_API_KEY=your-cohere-api-key
# JINA_API_KEY=your-jina-api-key

# UNSTRUCTURED_API_KEY=your-unstructured-api-key
# UNSTRUCTURED_API_URL=https://api.unstructured.io/general/v0/general

# LLAMA_PARSE_API_KEY=your-llamaparse-api-key

# Configuration files path
BRAIN_CONFIG_PATH=config/retrieval_config_workflow.yaml
CHAT_LLM_CONFIG_PATH=config/chat_llm_config.yaml

# LangSmith
# LANGCHAIN_TRACING_V2=true
# LANGCHAIN_ENDPOINT="https://api.smith.langchain.com"
# LANGCHAIN_API_KEY=your-langchain-api-key
# LANGCHAIN_PROJECT=your-langchain-project-name

# LOCAL
# OLLAMA_API_BASE_URL=http://host.docker.internal:11434 # Uncomment to activate ollama. This is the local url for the ollama api
Expand Down Expand Up @@ -32,7 +54,6 @@ EXTERNAL_SUPABASE_URL=http://localhost:54321
SUPABASE_SERVICE_KEY=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZS1kZW1vIiwicm9sZSI6InNlcnZpY2Vfcm9sZSIsImV4cCI6MTk4MzgxMjk5Nn0.EGIM96RAZx35lJzdJsyH-qQwv8Hdp7fsn3W0YpN81IU
PG_DATABASE_URL=postgresql://postgres:[email protected]:54322/postgres
PG_DATABASE_ASYNC_URL=postgresql+asyncpg://postgres:[email protected]:54322/postgres
ANTHROPIC_API_KEY=null
JWT_SECRET_KEY=super-secret-jwt-token-with-at-least-32-characters-long
AUTHENTICATE=true
TELEMETRY_ENABLED=true
Expand All @@ -41,7 +62,6 @@ CELEBRY_BROKER_QUEUE_NAME=quivr-preview.fifo
QUIVR_DOMAIN=http://localhost:3000/
BACKEND_URL=http://localhost:5050
EMBEDDING_DIM=1536
#COHERE_API_KEY=CHANGE_ME
DEACTIVATE_STRIPE=true

#RESEND
Expand Down
6 changes: 4 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -21,17 +21,19 @@ repos:
hooks:
# Run the linter.
- id: ruff
args: [--fix]
args: [--fix, --isolated]
additional_dependencies: []
# Run the formatter.
- id: ruff-format
args: [--isolated]
additional_dependencies: []
- repo: https://github.com/pre-commit/mirrors-mypy
rev: v1.10.1
hooks:
- id: mypy
name: mypy
additional_dependencies: ["types-aiofiles"]
args: ["--ignore-missing-imports", "--no-incremental", "--follow-imports=skip"]
additional_dependencies: ["types-aiofiles", "types-pyyaml", "pydantic", "sqlmodel"]
ci:
autofix_commit_msg: |
[pre-commit.ci] auto fixes from pre-commit.com hooks
Expand Down
15 changes: 3 additions & 12 deletions .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
"**/.docusaurus/": true,
"**/node_modules/": true
},
"json.sortOnSave.enable": true,
"[python]": {
"editor.defaultFormatter": "charliermarsh.ruff",
"editor.formatOnSave": true,
Expand All @@ -25,19 +24,10 @@
"source.fixAll": "explicit"
}
},
"python.formatting.provider": "black",
"python.analysis.extraPaths": [
"./backend"
],
"python.sortImports.path": "isort",
"python.linting.mypyEnabled": true,
"python.defaultInterpreterPath": "python3",
"python.linting.enabled": true,
"python.linting.flake8Enabled": true,
"python.linting.pycodestyleEnabled": true,
"python.linting.pylintEnabled": true,
"python.linting.pycodestyleCategorySeverity.W": "Error",
"python.linting.flake8CategorySeverity.W": "Error",
"python.testing.pytestArgs": [
"-v",
"--color=yes",
Expand All @@ -53,5 +43,6 @@
"reportMissingImports": "error",
"reportUnusedImport": "warning",
"reportGeneralTypeIssues": "warning"
}
}
},
"makefile.configureOnOpen": false
}
2 changes: 2 additions & 0 deletions backend/Dockerfile.dev
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@ COPY core/pyproject.toml core/README.md ./core/
COPY core/quivr_core/__init__.py ./core/quivr_core/__init__.py
COPY worker/pyproject.toml worker/README.md ./worker/
COPY worker/quivr_worker/__init__.py ./worker/quivr_worker/__init__.py
COPY core/MegaParse/pyproject.toml core/MegaParse/README.md ./core/MegaParse/
COPY core/MegaParse/megaparse/__init__.py ./core/MegaParse/megaparse/__init__.py

RUN PYTHONDONTWRITEBYTECODE=1 pip install --no-cache-dir -r requirements.lock

Expand Down
21 changes: 9 additions & 12 deletions backend/api/quivr_api/modules/brain/entity/brain_entity.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from uuid import UUID

from pydantic import BaseModel
from quivr_core.config import BrainConfig
from sqlalchemy.dialects.postgresql import ENUM as PGEnum
from sqlalchemy.ext.asyncio import AsyncAttrs
from sqlmodel import TIMESTAMP, Column, Field, Relationship, SQLModel, text
Expand Down Expand Up @@ -58,43 +59,39 @@ class Brain(AsyncAttrs, SQLModel, table=True):
default=BrainType.integration,
),
)
brain_chat_history: List["ChatHistory"] = Relationship( # noqa: F821
brain_chat_history: List["ChatHistory"] = Relationship( # type: ignore # noqa: F821
back_populates="brain", sa_relationship_kwargs={"lazy": "select"}
)
prompt_id: UUID | None = Field(default=None, foreign_key="prompts.id")
prompt: Prompt | None = Relationship( # noqa: f821
prompt: Prompt | None = Relationship( # noqa: F821
back_populates="brain", sa_relationship_kwargs={"lazy": "joined"}
)
knowledges: List[KnowledgeDB] = Relationship(
back_populates="brains", link_model=KnowledgeBrain
)


# TODO : add
# "meaning" "public"."vector",
# "tags" "public"."tags"[]


class BrainEntity(BaseModel):
brain_id: UUID
name: str
class BrainEntity(BrainConfig):
last_update: datetime | None = None
brain_type: BrainType | None = None
description: Optional[str] = None
temperature: Optional[float] = None
meaning: Optional[str] = None
AmineDiro marked this conversation as resolved.
Show resolved Hide resolved
openai_api_key: Optional[str] = None
tags: Optional[List[str]] = None
model: Optional[str] = None
max_tokens: Optional[int] = None
status: Optional[str] = None
prompt_id: Optional[UUID] = None
last_update: datetime
brain_type: BrainType
integration: Optional[IntegrationEntity] = None
integration_description: Optional[IntegrationDescriptionEntity] = None
snippet_emoji: Optional[str] = None
snippet_color: Optional[str] = None

@property
def id(self) -> UUID:
return self.brain_id

def dict(self, **kwargs):
data = super().dict(
**kwargs,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ def delete_integration_brain(self, brain_id, user_id):

def get_integration_brain_by_type_integration(
self, integration_name
) -> List[IntegrationEntity]:
) -> List[IntegrationEntity] | None:
response = (
self.db.table("integrations_user")
.select("*, integrations ()")
Expand Down
33 changes: 4 additions & 29 deletions backend/api/quivr_api/modules/brain/service/brain_service.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Optional
from typing import Optional, Tuple, Dict
from uuid import UUID

from fastapi import HTTPException
Expand Down Expand Up @@ -54,7 +54,7 @@ def find_brain_from_question(
chat_id: UUID,
history,
vector_store: CustomSupabaseVectorStore,
) -> (Optional[BrainEntity], dict[str, str]):
) -> Tuple[Optional[BrainEntity], Dict[str, str]]:
"""Find the brain to use for a question.

Args:
Expand Down Expand Up @@ -106,12 +106,12 @@ def find_brain_from_question(
brain_id_to_use = list_brains[0]["id"]
brain_to_use = self.get_brain_by_id(brain_id_to_use)

return brain_to_use, metadata
return brain_to_use, metadata # type: ignore

def create_brain(
self,
user_id: UUID,
brain: Optional[CreateBrainProperties],
brain: CreateBrainProperties | None = None,
) -> BrainEntity:
if brain is None:
brain = CreateBrainProperties()
Expand Down Expand Up @@ -226,28 +226,3 @@ def get_brain_details(
)

return brain

def get_connected_brains(self, brain_id: UUID) -> list[BrainEntity]:
return self.composite_brains_connections_repository.get_connected_brains(
brain_id
)

def update_secret_value(
self,
user_id: UUID,
brain_id: UUID,
secret_name: str,
secret_value: str,
) -> None:
"""Update an existing secret."""
self.external_api_secrets_repository.delete_secret(
user_id=user_id,
brain_id=brain_id,
secret_name=secret_name,
)
self.external_api_secrets_repository.create_secret(
user_id=user_id,
brain_id=brain_id,
secret_name=secret_name,
secret_value=secret_value,
)
46 changes: 46 additions & 0 deletions backend/api/quivr_api/modules/chat/controller/chat/utils.py
Original file line number Diff line number Diff line change
@@ -1,15 +1,61 @@
import time
import os
from enum import Enum

from fastapi import HTTPException
from quivr_api.logger import get_logger
from quivr_api.modules.models.entity.model import Model
from quivr_api.modules.models.service.model_service import ModelService
from quivr_api.modules.user.entity.user_identity import UserIdentity
from quivr_api.modules.user.service.user_usage import UserUsage
from quivr_core.config import RetrievalConfig

logger = get_logger(__name__)


class RetrievalConfigPathEnv(Enum):
CHAT_WITH_LLM = ("CHAT_LLM_CONFIG_PATH", "chat_llm_config.yaml")
RAG = ("BRAIN_CONFIG_PATH", "config/retrieval_config_workflow.yaml")

@property
def env_var(self) -> str:
return self.value[0]

@property
def default_path(self) -> str:
return self.value[1]


def get_config_file_path(
config_path_env: RetrievalConfigPathEnv, current_path: str | None = None
) -> str:
# Get the environment variable or fallback to the default path
_path = os.getenv(config_path_env.env_var, config_path_env.default_path)

if not current_path:
return _path

return os.path.join(current_path, _path)


def load_and_merge_retrieval_configuration(
config_file_path: str, sqlmodel: Model
) -> RetrievalConfig:
retrieval_config = RetrievalConfig.from_yaml(config_file_path)
field_mapping = {
"env_variable_name": "env_variable_name",
"endpoint_url": "llm_base_url",
}

retrieval_config.llm_config.set_from_sqlmodel(
sqlmodel=sqlmodel, mapping=field_mapping
)

retrieval_config.llm_config.set_llm_model(sqlmodel.name)

return retrieval_config


# TODO: rewrite
async def find_model_and_generate_metadata(
brain_model: str | None,
Expand Down
Loading
Loading