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/multi llm enhancement #412

Merged
merged 62 commits into from
Jul 8, 2024
Merged
Show file tree
Hide file tree
Changes from 18 commits
Commits
Show all changes
62 commits
Select commit Hold shift + click to select a range
dec7c3b
added multiple llm profiles
jagadeeswaran-zipstack Jun 18, 2024
a7d2b99
made changes in coverage and combined output
jagadeeswaran-zipstack Jun 18, 2024
e8f66ec
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 18, 2024
ae9105a
sonar lint fix
jagadeeswaran-zipstack Jun 19, 2024
19b1c5c
Merge branch 'FEAT/multi-llm-enhancement' of github.com:Zipstack/unst…
jagadeeswaran-zipstack Jun 19, 2024
42fb895
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 19, 2024
6300c14
added index output viewer
jagadeeswaran-zipstack Jun 19, 2024
14a8bcf
Merge branch 'FEAT/multi-llm-enhancement' of github.com:Zipstack/unst…
jagadeeswaran-zipstack Jun 19, 2024
6a9ffe7
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 19, 2024
87472f4
pre-commit fixes
jagadeeswaran-zipstack Jun 20, 2024
f8afb4d
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 20, 2024
2b55564
pre-commit fixes
jagadeeswaran-zipstack Jun 20, 2024
5155497
Merge branch 'FEAT/multi-llm-enhancement' of github.com:Zipstack/unst…
jagadeeswaran-zipstack Jun 20, 2024
c210923
added timer and profile info bar
jagadeeswaran-zipstack Jun 20, 2024
2204036
Merge branch 'main' of github.com:Zipstack/unstract into FEAT/multi-l…
jagadeeswaran-zipstack Jun 20, 2024
d7271ba
added profile limit
jagadeeswaran-zipstack Jun 20, 2024
ff9fe34
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 20, 2024
ffeee88
Trigger Build
jagadeeswaran-zipstack Jun 20, 2024
953349a
code clean up
jagadeeswaran-zipstack Jun 21, 2024
da06669
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 21, 2024
893bdb8
code clean up
jagadeeswaran-zipstack Jun 21, 2024
aea367b
Merge branch 'FEAT/multi-llm-enhancement' of github.com:Zipstack/unst…
jagadeeswaran-zipstack Jun 21, 2024
1b4367b
code optimization
jagadeeswaran-zipstack Jun 24, 2024
6f1b3be
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 24, 2024
2736986
removed unwanted f-strings
jagadeeswaran-zipstack Jun 24, 2024
a205033
Merge branch 'FEAT/multi-llm-enhancement' of github.com:Zipstack/unst…
jagadeeswaran-zipstack Jun 24, 2024
b2ab964
[pre-commit.ci] auto fixes from pre-commit.com hooks
pre-commit-ci[bot] Jun 24, 2024
942c96f
added ttl to settings and updated sample.env
jagadeeswaran-zipstack Jun 25, 2024
7f579a8
Merge branch 'FEAT/multi-llm-enhancement' of github.com:Zipstack/unst…
jagadeeswaran-zipstack Jun 25, 2024
9088beb
added types for func
jagadeeswaran-zipstack Jun 26, 2024
edc4022
single pass fix
jagadeeswaran-zipstack Jun 27, 2024
59e024c
Merge branch 'main' of github.com:Zipstack/unstract into FEAT/multi-l…
jagadeeswaran-zipstack Jun 27, 2024
8ba2627
added total cost and context to UI
jagadeeswaran-zipstack Jun 27, 2024
c760a6e
code refactor
jagadeeswaran-zipstack Jun 27, 2024
ffc6420
fix sonar issue
jagadeeswaran-zipstack Jun 27, 2024
29ad0d2
code refactor
jagadeeswaran-zipstack Jun 27, 2024
131c9fc
code refactor
jagadeeswaran-zipstack Jun 27, 2024
42831af
FE build fix
jagadeeswaran-zipstack Jun 27, 2024
3f86aff
re-index fix
jagadeeswaran-zipstack Jun 28, 2024
ccb3969
remove defaults for env
jagadeeswaran-zipstack Jun 28, 2024
f119d0a
changed display text for chuck
jagadeeswaran-zipstack Jun 28, 2024
283109e
Merge branch 'main' into FEAT/multi-llm-enhancement
jagadeeswaran-zipstack Jun 28, 2024
4782a84
code refactor
jagadeeswaran-zipstack Jun 28, 2024
d9ae4e4
Merge branch 'FEAT/multi-llm-enhancement' of github.com:Zipstack/unst…
jagadeeswaran-zipstack Jun 28, 2024
58d87f3
code refactor
jagadeeswaran-zipstack Jun 28, 2024
9268899
Update backend/prompt_studio/prompt_studio_core/views.py
chandrasekharan-zipstack Jun 28, 2024
56b109e
changed context db type to textfield
jagadeeswaran-zipstack Jun 30, 2024
52bdc24
Merge branch 'FEAT/multi-llm-enhancement' of github.com:Zipstack/unst…
jagadeeswaran-zipstack Jun 30, 2024
54f9d3a
Merge branch 'main' into FEAT/multi-llm-enhancement
nehabagdia Jul 2, 2024
2552f58
Merge branch 'main' of github.com:Zipstack/unstract into FEAT/multi-l…
jagadeeswaran-zipstack Jul 3, 2024
62ca5e2
simple prompt studio compatability fix
jagadeeswaran-zipstack Jul 3, 2024
afd9a18
Merge branch 'FEAT/multi-llm-enhancement' of github.com:Zipstack/unst…
jagadeeswaran-zipstack Jul 3, 2024
394210a
moved polling to FE
jagadeeswaran-zipstack Jul 3, 2024
7a2b92d
sonar fix
jagadeeswaran-zipstack Jul 3, 2024
3791bd4
sonar fix for nesting
jagadeeswaran-zipstack Jul 3, 2024
2d44a17
move polling to static func
jagadeeswaran-zipstack Jul 3, 2024
5d18ac2
added enum for index status
jagadeeswaran-zipstack Jul 4, 2024
65fa454
Merge branch 'main' into FEAT/multi-llm-enhancement
jagadeeswaran-zipstack Jul 4, 2024
f4c8ac3
reduced timeout value for polling
jagadeeswaran-zipstack Jul 5, 2024
0b2d124
Merge branch 'FEAT/multi-llm-enhancement' of github.com:Zipstack/unst…
jagadeeswaran-zipstack Jul 5, 2024
cbc60cd
FE code cleanup
jagadeeswaran-zipstack Jul 5, 2024
d438d8e
Merge branch 'main' into FEAT/multi-llm-enhancement
tahierhussain Jul 8, 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
4 changes: 4 additions & 0 deletions backend/adapter_processor/serializers.py
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,10 @@ def to_representation(self, instance: AdapterInstance) -> dict[str, str]:
rep[common.ICON] = AdapterProcessor.get_adapter_data_with_key(
instance.adapter_id, common.ICON
)
adapter_metadata = instance.get_adapter_meta_data()
model = adapter_metadata.get("model")
if model:
rep["model"] = adapter_metadata["model"]
jagadeeswaran-zipstack marked this conversation as resolved.
Show resolved Hide resolved
jagadeeswaran-zipstack marked this conversation as resolved.
Show resolved Hide resolved

if instance.is_friction_less:
rep["created_by_email"] = "Unstract"
Expand Down
1 change: 1 addition & 0 deletions backend/prompt_studio/prompt_profile_manager/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ class ProfileManagerKeys:
VECTOR_STORE = "vector_store"
EMBEDDING_MODEL = "embedding_model"
X2TEXT = "x2text"
PROMPT_STUDIO_TOOL = "prompt_studio_tool"


class ProfileManagerErrors:
Expand Down
1 change: 1 addition & 0 deletions backend/prompt_studio/prompt_studio_core/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ class ToolStudioPromptKeys:
NOTES = "NOTES"
OUTPUT = "output"
SEQUENCE_NUMBER = "sequence_number"
PROFILE_MANAGER_ID = "profile_manager"


class FileViewTypes:
Expand Down
7 changes: 7 additions & 0 deletions backend/prompt_studio/prompt_studio_core/exceptions.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,3 +58,10 @@ class PermissionError(APIException):
class EmptyPromptError(APIException):
status_code = 422
default_detail = "Prompt(s) cannot be empty"


class MaxProfilesReachedError(APIException):
status_code = 403
default_detail = (
"Maximum number of profiles (max 4) per prompt studio project has been reached."
jagadeeswaran-zipstack marked this conversation as resolved.
Show resolved Hide resolved
)
91 changes: 77 additions & 14 deletions backend/prompt_studio/prompt_studio_core/prompt_studio_helper.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import json
import logging
import os
import time
import uuid
from pathlib import Path
from typing import Any, Optional
Expand All @@ -27,6 +28,12 @@
)
from prompt_studio.prompt_studio_core.models import CustomTool
from prompt_studio.prompt_studio_core.prompt_ide_base_tool import PromptIdeBaseTool
from prompt_studio.prompt_studio_core.redis_utils import (
get_indexed_document_id,
is_document_indexing,
mark_document_indexed,
set_document_indexing,
)
from prompt_studio.prompt_studio_document_manager.models import DocumentManager
from prompt_studio.prompt_studio_index_manager.prompt_studio_index_helper import ( # noqa: E501
PromptStudioIndexHelper,
Expand Down Expand Up @@ -364,6 +371,7 @@ def prompt_responder(
document_id: str,
id: Optional[str] = None,
run_id: str = None,
profile_manager_id: Optional[str] = None,
) -> Any:
"""Execute chain/single run of the prompts. Makes a call to prompt
service and returns the dict of response.
Expand All @@ -374,6 +382,7 @@ def prompt_responder(
user_id (str): User's ID
document_id (str): UUID of the document uploaded
id (Optional[str]): ID of the prompt
profile_manager_id (Optional[str]): UUID of the profile manager

Raises:
AnswerFetchError: Error from prompt-service
Expand Down Expand Up @@ -442,6 +451,7 @@ def prompt_responder(
org_id=org_id,
document_id=document_id,
run_id=run_id,
profile_manager_id=profile_manager_id,
)

OutputManagerHelper.handle_prompt_output_update(
Expand All @@ -450,6 +460,8 @@ def prompt_responder(
outputs=response["output"],
document_id=document_id,
is_single_pass_extract=False,
profile_manager_id=profile_manager_id,
tool=tool,
)
# TODO: Review if this catch-all is required
except Exception as e:
Expand Down Expand Up @@ -562,6 +574,7 @@ def _fetch_response(
org_id: str,
document_id: str,
run_id: str,
profile_manager_id: Optional[str] = None,
) -> Any:
"""Utility function to invoke prompt service. Used internally.

Expand All @@ -572,6 +585,8 @@ def _fetch_response(
prompt (ToolStudioPrompt): ToolStudioPrompt instance to fetch response
org_id (str): UUID of the organization
document_id (str): UUID of the document
profile_manager_id (Optional[str]): UUID of the profile manager


Raises:
DefaultProfileError: If no default profile is selected
Expand All @@ -580,6 +595,20 @@ def _fetch_response(
Returns:
Any: Output from LLM
"""

# Fetch the ProfileManager instance using the profile_manager_id if provided
if profile_manager_id:
chandrasekharan-zipstack marked this conversation as resolved.
Show resolved Hide resolved
try:
profile_manager = ProfileManager.objects.get(
jagadeeswaran-zipstack marked this conversation as resolved.
Show resolved Hide resolved
profile_id=profile_manager_id
)
except ProfileManager.DoesNotExist:
raise DefaultProfileError(
f"ProfileManager with ID {profile_manager_id} does not exist."
jagadeeswaran-zipstack marked this conversation as resolved.
Show resolved Hide resolved
)
else:
profile_manager = prompt.profile_manager

monitor_llm_instance: Optional[AdapterInstance] = tool.monitor_llm
monitor_llm: Optional[str] = None
challenge_llm_instance: Optional[AdapterInstance] = tool.challenge_llm
Expand All @@ -600,21 +629,20 @@ def _fetch_response(
challenge_llm = str(default_profile.llm.id)

# Need to check the user who created profile manager
PromptStudioHelper.validate_adapter_status(prompt.profile_manager)
PromptStudioHelper.validate_adapter_status(profile_manager)
# Need to check the user who created profile manager
# has access to adapters
PromptStudioHelper.validate_profile_manager_owner_access(prompt.profile_manager)
PromptStudioHelper.validate_profile_manager_owner_access(profile_manager)
# Not checking reindex here as there might be
# change in Profile Manager
vector_db = str(prompt.profile_manager.vector_store.id)
embedding_model = str(prompt.profile_manager.embedding_model.id)
llm = str(prompt.profile_manager.llm.id)
x2text = str(prompt.profile_manager.x2text.id)
prompt_profile_manager: ProfileManager = prompt.profile_manager
if not prompt_profile_manager:
vector_db = str(profile_manager.vector_store.id)
embedding_model = str(profile_manager.embedding_model.id)
llm = str(profile_manager.llm.id)
x2text = str(profile_manager.x2text.id)
if not profile_manager:
raise DefaultProfileError()
PromptStudioHelper.dynamic_indexer(
profile_manager=prompt_profile_manager,
profile_manager=profile_manager,
file_path=doc_path,
tool_id=str(tool.tool_id),
org_id=org_id,
Expand All @@ -639,16 +667,16 @@ def _fetch_response(

output[TSPKeys.PROMPT] = prompt.prompt
output[TSPKeys.ACTIVE] = prompt.active
output[TSPKeys.CHUNK_SIZE] = prompt.profile_manager.chunk_size
output[TSPKeys.CHUNK_SIZE] = profile_manager.chunk_size
output[TSPKeys.VECTOR_DB] = vector_db
output[TSPKeys.EMBEDDING] = embedding_model
output[TSPKeys.CHUNK_OVERLAP] = prompt.profile_manager.chunk_overlap
output[TSPKeys.CHUNK_OVERLAP] = profile_manager.chunk_overlap
output[TSPKeys.LLM] = llm
output[TSPKeys.TYPE] = prompt.enforce_type
output[TSPKeys.NAME] = prompt.prompt_key
output[TSPKeys.RETRIEVAL_STRATEGY] = prompt.profile_manager.retrieval_strategy
output[TSPKeys.SIMILARITY_TOP_K] = prompt.profile_manager.similarity_top_k
output[TSPKeys.SECTION] = prompt.profile_manager.section
output[TSPKeys.RETRIEVAL_STRATEGY] = profile_manager.retrieval_strategy
output[TSPKeys.SIMILARITY_TOP_K] = profile_manager.similarity_top_k
output[TSPKeys.SECTION] = profile_manager.section
output[TSPKeys.X2TEXT_ADAPTER] = x2text
# Eval settings for the prompt
output[TSPKeys.EVAL_SETTINGS] = {}
Expand Down Expand Up @@ -750,9 +778,43 @@ def dynamic_indexer(
profile_manager.chunk_size = 0

try:

usage_kwargs = {"run_id": run_id}
util = PromptIdeBaseTool(log_level=LogLevel.INFO, org_id=org_id)
tool_index = Index(tool=util)
doc_id_key = tool_index.generate_file_id(
tool_id=tool_id,
vector_db=vector_db,
embedding=embedding_model,
x2text=x2text_adapter,
chunk_size=str(profile_manager.chunk_size),
chunk_overlap=str(profile_manager.chunk_overlap),
file_path=file_path,
file_hash=None,
)
indexed_doc_id = get_indexed_document_id(doc_id_key)
if indexed_doc_id:
return indexed_doc_id

# Polling if document is already being indexed
if is_document_indexing(doc_id_key):
max_wait_time = 1800 # 30 minutes
wait_time = 0
polling_interval = 5 # Poll every 5 seconds
jagadeeswaran-zipstack marked this conversation as resolved.
Show resolved Hide resolved
while is_document_indexing(doc_id_key):
jagadeeswaran-zipstack marked this conversation as resolved.
Show resolved Hide resolved
if wait_time >= max_wait_time:
jagadeeswaran-zipstack marked this conversation as resolved.
Show resolved Hide resolved
raise IndexingAPIError(
"Indexing timed out. Please try again later."
)
time.sleep(polling_interval)
jagadeeswaran-zipstack marked this conversation as resolved.
Show resolved Hide resolved
wait_time += polling_interval

# After waiting, check if the document is indexed
indexed_doc_id = get_indexed_document_id(doc_id_key)
if indexed_doc_id:
return indexed_doc_id
# Set the document as being indexed
set_document_indexing(doc_id_key)
jagadeeswaran-zipstack marked this conversation as resolved.
Show resolved Hide resolved
doc_id: str = tool_index.index(
tool_id=tool_id,
embedding_instance_id=embedding_model,
Expand All @@ -772,6 +834,7 @@ def dynamic_indexer(
profile_manager=profile_manager,
doc_id=doc_id,
)
mark_document_indexed(doc_id_key, doc_id)
return doc_id
except (IndexingError, IndexingAPIError, SdkError) as e:
doc_name = os.path.split(file_path)[1]
Expand Down
24 changes: 24 additions & 0 deletions backend/prompt_studio/prompt_studio_core/redis_utils.py
jagadeeswaran-zipstack marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
from utils.cache_service import CacheService


def set_document_indexing(doc_id_key, ttl=1800):
CacheService.set_key(f"document_indexing:{doc_id_key}", "started", expire=ttl)


def is_document_indexing(doc_id_key):
return CacheService.get_key(f"document_indexing:{doc_id_key}") == b"started"


def mark_document_indexed(doc_id_key, doc_id):
CacheService.set_key(f"document_indexing:{doc_id_key}", doc_id, expire=3600)
jagadeeswaran-zipstack marked this conversation as resolved.
Show resolved Hide resolved


def get_indexed_document_id(doc_id_key):
result = CacheService.get_key(f"document_indexing:{doc_id_key}")
if result and result != b"started":
return result
return None


def remove_document_indexing(doc_id_key):
CacheService.delete_a_key(f"document_indexing:{doc_id_key}")
30 changes: 29 additions & 1 deletion backend/prompt_studio/prompt_studio_core/views.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@
from file_management.file_management_helper import FileManagerHelper
from permissions.permission import IsOwner, IsOwnerOrSharedUser
from prompt_studio.processor_loader import ProcessorConfig, load_plugins
from prompt_studio.prompt_profile_manager.constants import ProfileManagerErrors
from prompt_studio.prompt_profile_manager.constants import (
ProfileManagerErrors,
ProfileManagerKeys,
)
from prompt_studio.prompt_profile_manager.models import ProfileManager
from prompt_studio.prompt_profile_manager.serializers import ProfileManagerSerializer
from prompt_studio.prompt_studio.constants import ToolStudioPromptErrors
Expand All @@ -23,13 +26,16 @@
)
from prompt_studio.prompt_studio_core.exceptions import (
IndexingAPIError,
MaxProfilesReachedError,
ToolDeleteError,
)
from prompt_studio.prompt_studio_core.prompt_studio_helper import PromptStudioHelper
from prompt_studio.prompt_studio_core.redis_utils import remove_document_indexing
from prompt_studio.prompt_studio_document_manager.models import DocumentManager
from prompt_studio.prompt_studio_document_manager.prompt_studio_document_helper import ( # noqa: E501
PromptStudioDocumentHelper,
)
from prompt_studio.prompt_studio_index_manager.models import IndexManager
from prompt_studio.prompt_studio_registry.prompt_studio_registry_helper import (
PromptStudioRegistryHelper,
)
Expand Down Expand Up @@ -264,6 +270,9 @@ def fetch_response(self, request: HttpRequest, pk: Any = None) -> Response:
document_id: str = request.data.get(ToolStudioPromptKeys.DOCUMENT_ID)
id: str = request.data.get(ToolStudioPromptKeys.ID)
run_id: str = request.data.get(ToolStudioPromptKeys.RUN_ID)
profile_manager: str = request.data.get(
ToolStudioPromptKeys.PROFILE_MANAGER_ID, None
jagadeeswaran-zipstack marked this conversation as resolved.
Show resolved Hide resolved
jagadeeswaran-zipstack marked this conversation as resolved.
Show resolved Hide resolved
)
if not run_id:
# Generate a run_id
run_id = CommonUtils.generate_uuid()
Expand All @@ -275,6 +284,7 @@ def fetch_response(self, request: HttpRequest, pk: Any = None) -> Response:
user_id=custom_tool.created_by.user_id,
document_id=document_id,
run_id=run_id,
profile_manager_id=profile_manager,
)
return Response(response, status=status.HTTP_200_OK)

Expand Down Expand Up @@ -339,6 +349,17 @@ def create_profile_manager(self, request: HttpRequest, pk: Any = None) -> Respon
serializer = ProfileManagerSerializer(data=request.data, context=context)

serializer.is_valid(raise_exception=True)

# Check for the maximum number of profiles constraint
prompt_studio_tool = serializer.validated_data[
ProfileManagerKeys.PROMPT_STUDIO_TOOL
]
profile_count = ProfileManager.objects.filter(
prompt_studio_tool=prompt_studio_tool
).count()

if profile_count >= 4:
jagadeeswaran-zipstack marked this conversation as resolved.
Show resolved Hide resolved
raise MaxProfilesReachedError()
jagadeeswaran-zipstack marked this conversation as resolved.
Show resolved Hide resolved
jagadeeswaran-zipstack marked this conversation as resolved.
Show resolved Hide resolved
try:
self.perform_create(serializer)
except IntegrityError:
Expand Down Expand Up @@ -457,6 +478,11 @@ def delete_for_ide(self, request: HttpRequest, pk: uuid) -> Response:
path = file_path
file_system = LocalStorageFS(settings={"path": path})
try:
# Delete indexed flags in redis
index_managers = IndexManager.objects.filter(document_manager=document_id)
for index_manager in index_managers:
raw_index_id = index_manager.raw_index_id
remove_document_indexing(raw_index_id)
# Delete the document record
document.delete()
# Delete the files
Expand All @@ -466,6 +492,8 @@ def delete_for_ide(self, request: HttpRequest, pk: uuid) -> Response:
FileManagerHelper.delete_related_files(
file_system, path, file_name, directories
)
# Delete indexed flags in redis

chandrasekharan-zipstack marked this conversation as resolved.
Show resolved Hide resolved
return Response(
{"data": "File deleted succesfully."},
status=status.HTTP_200_OK,
Expand Down
8 changes: 8 additions & 0 deletions backend/prompt_studio/prompt_studio_index_manager/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@
"get": "retrieve",
}
)
prompt_studio_index_data = IndexManagerView.as_view(
{"get": "get_indexed_data_for_profile"}
)

urlpatterns = format_suffix_patterns(
[
Expand All @@ -18,5 +21,10 @@
prompt_studio_index_list,
name="prompt-studio-documents-list",
),
path(
"indexed-result/",
prompt_studio_index_data,
name="prompt-studio-indexed-list",
),
]
)
Loading
Loading