Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
114 commits
Select commit Hold shift + click to select a range
f4fc127
convert to using blocks for llama index ChatMessages
jkwatson Jul 10, 2025
6fbba3a
also use ChatMessage for direct chat
jkwatson Jul 10, 2025
3ebe708
use text blocks for a couple more cases
jkwatson Jul 10, 2025
37968e8
update a couple llama index libs
jkwatson Jul 10, 2025
9405431
fix a bug when data source summary is none
baasitsharief Jul 10, 2025
91cf234
add in image generation
ewilliams-cloudera Jul 11, 2025
d9498ef
ai generated tests for the evaluators functions
jkwatson Jul 15, 2025
9677012
don't try to look up node ids in empty vector stores
jkwatson Jul 16, 2025
49bbdb1
move suggested questions under the sessions route
jkwatson Jul 16, 2025
19f0f43
fix things up for postgres db access
jkwatson Jul 16, 2025
b0c6f3c
formatting
jkwatson Jul 16, 2025
50c1f3f
fixes for not being able to create new dbs
jkwatson Jul 16, 2025
b343124
only set the DB_URL if it isn't already set
jkwatson Jul 17, 2025
77e14e1
fix the install directory
jkwatson Jul 17, 2025
0d83634
change location of .nvm and source bash from install_node
ewilliams-cloudera Jul 17, 2025
cd202de
Update release version to dev-testing
actions-user Jul 17, 2025
3b91002
removed unused import
ewilliams-cloudera Jul 17, 2025
159b37d
add logging for initializing the JDBI instance
jkwatson Jul 17, 2025
1150fbc
Update release version to dev-testing
actions-user Jul 17, 2025
51b6337
wip on ui for metadata
ewilliams-cloudera Jul 17, 2025
af8d53f
wip
jkwatson Jul 17, 2025
3ebf3c5
update FE types to match python land
jkwatson Jul 17, 2025
d10843b
fix margin bottom consistency
ewilliams-cloudera Jul 17, 2025
c3f2e2e
Update release version to dev-testing
actions-user Jul 17, 2025
497c80b
set the username/password for the database if set from env
jkwatson Jul 17, 2025
0c9b0a2
drop databases
mliu-cloudera Jul 17, 2025
c3de72a
Update release version to dev-testing
actions-user Jul 17, 2025
3f7ac6e
limit number of retries
ewilliams-cloudera Jul 17, 2025
7d4ebfd
Update release version to dev-testing
actions-user Jul 17, 2025
168e79d
bumped bedrock converse and fixed a bug in tool calling check
baasitsharief Jul 17, 2025
b7f61af
remove unused
ewilliams-cloudera Jul 17, 2025
1239b59
Update release version to dev-testing
actions-user Jul 17, 2025
40b80c7
minor error handling improvement
ewilliams-cloudera Jul 17, 2025
0420092
fixed bug with Empty Response with no documents in data source and to…
baasitsharief Jul 17, 2025
4b27d11
fix mypy issues
baasitsharief Jul 17, 2025
9fe4cd2
add a main method to test if a db connection string is valid
jkwatson Jul 18, 2025
b1a5b48
Update release version to dev-testing
actions-user Jul 18, 2025
80c55b6
add python endpoint to test a jdbc connection string
jkwatson Jul 18, 2025
29f635a
export the install dir so it can be used by the fastapi process
jkwatson Jul 18, 2025
7f96771
make sure to use the right java
jkwatson Jul 18, 2025
336fc40
pass in the db type so we can do a bare server connection
jkwatson Jul 18, 2025
eb50cea
Update release version to dev-testing
actions-user Jul 18, 2025
41da0cf
better error handling for api proxy
ewilliams-cloudera Jul 18, 2025
019e04f
pass through error on non-502s, use 502 error instaed of 503, dont re…
ewilliams-cloudera Jul 18, 2025
809020b
Update release version to dev-testing
actions-user Jul 18, 2025
4d0b691
more config details
ewilliams-cloudera Jul 18, 2025
411e398
wip settings page for external metadata db
baasitsharief Jul 18, 2025
2069e16
fix
baasitsharief Jul 18, 2025
8b7bf34
drop databases
mliu-cloudera Jul 18, 2025
ffc56c7
wip on formatting warnings
jkwatson Jul 18, 2025
3edbb91
wip
ewilliams-cloudera Jul 18, 2025
fc809f7
wip test connection
baasitsharief Jul 18, 2025
4aefce8
update form items
ewilliams-cloudera Jul 18, 2025
a3e832c
fix connection test
ewilliams-cloudera Jul 20, 2025
2d2b39b
use formValues
ewilliams-cloudera Jul 20, 2025
c0c1ab4
refactor: make username and password required for JDBC connection
baasitsharief Jul 21, 2025
80aaf1a
add image generation tools for OpenAI and Bedrock
baasitsharief Jul 21, 2025
5aeb2fc
Define valid images sizes
mliu-cloudera Jul 21, 2025
4b410e3
Add support for Titan and Stable Diffusion image generation models
baasitsharief Jul 21, 2025
00ca830
Add Bedrock support for Stable Diffusion and Titan image generation t…
baasitsharief Jul 21, 2025
56199c7
improve handling for testing connection
ewilliams-cloudera Jul 22, 2025
8875d10
conditionally render test button
ewilliams-cloudera Jul 22, 2025
d8ec481
fix mypy issues
ewilliams-cloudera Jul 22, 2025
8487d7b
disable test button if no password or username
ewilliams-cloudera Jul 22, 2025
d0ea0cf
Update ui/src/pages/Settings/MetadataDBFields.tsx
ewilliams-cloudera Jul 22, 2025
5be6d10
Update ui/src/pages/Settings/MetadataDBFields.tsx
ewilliams-cloudera Jul 22, 2025
24880bd
Update release version to dev-testing
actions-user Jul 22, 2025
b95de5d
handle clearing values for external db when switching to h2
ewilliams-cloudera Jul 22, 2025
fdc3df1
clear field values in ui when using h2
ewilliams-cloudera Jul 22, 2025
d718e97
Update release version to dev-testing
actions-user Jul 22, 2025
43c4b8d
refactor environment variable handling for H2 database configuration
ewilliams-cloudera Jul 22, 2025
39ad030
Update release version to dev-testing
actions-user Jul 22, 2025
5953591
refactor: update H2 database URL to use absolute path
baasitsharief Jul 22, 2025
5dc9c57
refactor: change metadata_db_provider comparison to string literal fo…
baasitsharief Jul 22, 2025
9e008a0
refactor: fix comparison operator for metadata_db_provider in H2 check
baasitsharief Jul 22, 2025
7bb9391
Update release version to dev-testing
actions-user Jul 22, 2025
e91cef1
refactor: remove DB_URL, DB_USERNAME, and DB_PASSWORD from environmen…
baasitsharief Jul 22, 2025
016f5df
Update release version to dev-testing
actions-user Jul 22, 2025
2e71996
refactor: update config_to_env to use Optional for environment variab…
baasitsharief Jul 22, 2025
cb8c84d
refactor: change config_to_env to return non-optional environment var…
baasitsharief Jul 22, 2025
b7277e4
Update release version to dev-testing
actions-user Jul 22, 2025
c9088a1
refactor: update DB_URL retrieval to use a fallback value for H2 conf…
baasitsharief Jul 22, 2025
e422d60
refactor: streamline JDBC configuration for H2 by using a default DB_…
baasitsharief Jul 22, 2025
6fc709b
refactor: improve validation message handling and remove messageQueue…
ewilliams-cloudera Jul 22, 2025
a65a696
Update release version to dev-testing
actions-user Jul 22, 2025
1c9e2d6
Use match-case for ModelSource
mliu-cloudera Jul 22, 2025
5c1a97b
refactor: enhance input validation for JDBC URL, username, and passwo…
baasitsharief Jul 22, 2025
83e0239
Reduce duplication
mliu-cloudera Jul 22, 2025
7d5e1d1
Merge branch 'mob/main' into mob/spike-multi-modal
baasitsharief Jul 22, 2025
60c49ad
Mostly satisfy mypy
mliu-cloudera Jul 22, 2025
1b9b5ce
Satisfy mypy
mliu-cloudera Jul 22, 2025
e0df455
Remove unused import
mliu-cloudera Jul 22, 2025
87fa320
Merge remote-tracking branch 'origin' into mob/spike-multi-modal
baasitsharief Jul 31, 2025
b3e4ece
feat: add ModelSource and get_model_source to module exports to fix w…
baasitsharief Jul 31, 2025
32ecf9a
fix: update API proxy port from 3000 to 8080 in Vite configuration
baasitsharief Jul 31, 2025
76cbe5f
feat: implement image generation tools based on model source selection
baasitsharief Jul 31, 2025
bbd1f47
refactor: remove unused import of BaseTool from image_generation.py
baasitsharief Aug 4, 2025
c801a47
refactor: update constructor parameters to use Optional for api_key a…
baasitsharief Aug 4, 2025
a6fe657
Merge branch 'main' into mob/spike-multi-modal
baasitsharief Aug 5, 2025
2504b9d
feat: add endpoint and query for retrieving available image generatio…
baasitsharief Aug 6, 2025
3dece7f
feat: add functionality to select and retrieve image generation tools
baasitsharief Aug 6, 2025
8d4d020
feat: update selected image generation tool handling to support null …
baasitsharief Aug 6, 2025
1447660
refactor: improve error logging format for selected image generation …
baasitsharief Aug 6, 2025
5cd2304
feat: refactor image generation tool handling to use a unified config…
baasitsharief Aug 6, 2025
5c4acf5
feat: update ToolsManager to handle session updates and refactor tool…
baasitsharief Aug 6, 2025
04bd88f
feat: add cache static file serving functionality to Vite and Express…
baasitsharief Aug 7, 2025
9d30b12
feat: enhance image handling in chat responses by removing sandbox pr…
baasitsharief Aug 11, 2025
62ebd42
Merge remote-tracking branch 'origin/main' into mob/spike-multi-modal
jkwatson Aug 11, 2025
92dcc34
refactor: remove unused regex import from chat service
baasitsharief Aug 11, 2025
3615f53
refactor: improve type definitions and clean up cache static plugin i…
baasitsharief Aug 11, 2025
cc2e6c3
fix: update valueFormatter to handle undefined values in Metrics comp…
baasitsharief Aug 11, 2025
25b6edb
serve images from llm-service/cache
ewilliams-cloudera Aug 12, 2025
384cffa
fix mypy
ewilliams-cloudera Aug 12, 2025
09c6e0c
Merge branch 'main' into mob/spike-multi-modal
baasitsharief Aug 12, 2025
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
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,4 +10,5 @@ databases/
**/.DS_Store
.history
addresses/
tools/mcp.json
tools/mcp.json
tools/image_generation_config.json
45 changes: 44 additions & 1 deletion llm-service/app/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
from typing import AsyncGenerator

import opik
from fastapi import FastAPI, Request, Response
from fastapi import FastAPI, Request, Response, HTTPException
from fastapi.middleware.cors import CORSMiddleware
from uvicorn.logging import DefaultFormatter

Expand Down Expand Up @@ -198,3 +198,46 @@ async def log_request_received(


app.include_router(index.router)

# Serve cached images via FastAPI from the llm-service/cache directory
_cache_dir = os.path.abspath(os.path.join(os.path.dirname(__file__), "..", "cache"))
os.makedirs(_cache_dir, exist_ok=True)


@app.get("/cache/{filename:path}")
async def serve_cached_image(filename: str) -> Response:
"""Serve cached images with proper MIME type detection."""
import mimetypes
from pathlib import Path

file_path = Path(_cache_dir) / filename

if not file_path.exists() or not file_path.is_file():
raise HTTPException(status_code=404, detail="Image not found")

# Determine MIME type based on file extension
mime_type, _ = mimetypes.guess_type(str(file_path))

# Fallback MIME types for common image formats
if not mime_type:
file_ext = file_path.suffix.lower()
mime_type = {
".png": "image/png",
".jpg": "image/jpeg",
".jpeg": "image/jpeg",
".gif": "image/gif",
".bmp": "image/bmp",
".webp": "image/webp",
".svg": "image/svg+xml",
".ico": "image/x-icon",
}.get(file_ext, "application/octet-stream")

# Read file content
with open(file_path, "rb") as f:
content = f.read()

return Response(
content=content,
media_type=mime_type,
headers={"Cache-Control": "public, max-age=31536000"}, # Cache for 1 year
)
202 changes: 199 additions & 3 deletions llm-service/app/routers/index/tools/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,15 +38,23 @@
import json
import logging
import os
import re
from typing import Any, Optional, cast, Annotated
from urllib.parse import unquote

import re
from fastapi import APIRouter, Header, HTTPException
from pydantic import BaseModel

from .... import exceptions
from ....config import settings
from ....services.models import get_model_source, ModelSource
from ....services.models.providers import BedrockModelProvider
from ....services.query.agents.agent_tools.image_generation import (
BEDROCK_STABLE_DIFFUSION_MODEL_ID,
BEDROCK_TITAN_IMAGE_MODEL_ID,
ImageGenerationTools,
IMAGE_GENERATION_TOOL_METADATA,
)
from ....services.utils import has_admin_rights

logger = logging.getLogger(__name__)
Expand Down Expand Up @@ -74,7 +82,19 @@ class Tool(ToolMetadata):
env: Optional[dict[str, str]] = None


class ImageGenerationConfig(BaseModel):
"""
Represents the complete image generation configuration.
"""

enabled: bool = True
selected_tool: Optional[str] = None


mcp_json_path: str = os.path.join(settings.tools_dir, "mcp.json")
image_generation_config_path: str = os.path.join(
settings.tools_dir, "image_generation_config.json"
)


def get_mcp_config() -> dict[str, Any]:
Expand All @@ -97,16 +117,182 @@ def get_mcp_config() -> dict[str, Any]:
)


def get_image_generation_config() -> ImageGenerationConfig:
"""
Gets the complete image generation configuration (enabled state + selected tool).
"""
if not os.path.exists(image_generation_config_path):
# Default configuration - disabled by default
return ImageGenerationConfig(enabled=False, selected_tool=None)

try:
with open(image_generation_config_path, "r") as f:
data = json.load(f)
return ImageGenerationConfig(**data)
except Exception:
logger.error(
"Failed to get image generation config from %s",
image_generation_config_path,
)
# Default configuration - disabled by default
return ImageGenerationConfig(enabled=False, selected_tool=None)


def set_image_generation_config(config: ImageGenerationConfig) -> None:
"""
Sets the complete image generation configuration.
"""
os.makedirs(os.path.dirname(image_generation_config_path), exist_ok=True)
with open(image_generation_config_path, "w") as f:
json.dump(config.model_dump(), f, indent=2)


def get_selected_image_generation_tool() -> Optional[str]:
"""
Gets the currently selected image generation tool.
Returns None if image generation is disabled or no tool is selected.
"""
config = get_image_generation_config()

if not config.enabled:
return None

return config.selected_tool


@router.get(
"",
summary="Returns a list of available tools.",
response_model=None,
)
@exceptions.propagates
def tools() -> list[ToolMetadata]:

# Get MCP tools from config
mcp_config = get_mcp_config()
return [ToolMetadata(**server) for server in mcp_config["mcp_servers"]]
tool_list = [ToolMetadata(**server) for server in mcp_config["mcp_servers"]]

return tool_list


def get_image_generation_tool_metadata() -> list[ToolMetadata]:
# Get current model provider
model_source = get_model_source()
# Add image generation tools based on the current model provider
if model_source == ModelSource.OPENAI:
tool_metadata = IMAGE_GENERATION_TOOL_METADATA[
ImageGenerationTools.OPENAI_IMAGE_GENERATION
]
return [
ToolMetadata(
name=ImageGenerationTools.OPENAI_IMAGE_GENERATION,
metadata={
"description": tool_metadata["description"],
"display_name": tool_metadata["display_name"],
},
)
]
if model_source == ModelSource.BEDROCK:
supported_model_ids = [
BEDROCK_STABLE_DIFFUSION_MODEL_ID,
BEDROCK_TITAN_IMAGE_MODEL_ID,
]
available_models = BedrockModelProvider.list_image_generation_models()
supported_bedrock_image_generation_tools = []
if not available_models:
return []
for model in available_models:
if model.model_id not in supported_model_ids:
continue
if model.model_id == BEDROCK_STABLE_DIFFUSION_MODEL_ID:
tool_metadata = IMAGE_GENERATION_TOOL_METADATA[
ImageGenerationTools.BEDROCK_STABLE_DIFFUSION
]
supported_bedrock_image_generation_tools.append(
ToolMetadata(
name=ImageGenerationTools.BEDROCK_STABLE_DIFFUSION,
metadata={
"description": tool_metadata["description"],
"display_name": tool_metadata["display_name"],
},
)
)
elif model.model_id == BEDROCK_TITAN_IMAGE_MODEL_ID:
tool_metadata = IMAGE_GENERATION_TOOL_METADATA[
ImageGenerationTools.BEDROCK_TITAN_IMAGE
]
supported_bedrock_image_generation_tools.append(
ToolMetadata(
name=ImageGenerationTools.BEDROCK_TITAN_IMAGE,
metadata={
"description": tool_metadata["description"],
"display_name": tool_metadata["display_name"],
},
)
)
return supported_bedrock_image_generation_tools
# Return empty list for other model providers
return []


@router.get(
"/image-generation",
summary="Returns a list of available image generation tools.",
response_model=list[ToolMetadata],
)
@exceptions.propagates
def image_generation_tools() -> list[ToolMetadata]:
"""
Returns a list of available image generation tools based on the current model provider.
"""
return get_image_generation_tool_metadata()


@router.get(
"/image-generation/config",
summary="Returns the complete image generation configuration.",
response_model=ImageGenerationConfig,
)
@exceptions.propagates
def get_image_generation_config_endpoint() -> ImageGenerationConfig:
"""
Returns the complete image generation configuration.
"""
return get_image_generation_config()


@router.post(
"/image-generation/config",
summary="Sets the complete image generation configuration.",
response_model=ImageGenerationConfig,
)
@exceptions.propagates
def set_image_generation_config_endpoint(
config: ImageGenerationConfig,
remote_user: Annotated[str | None, Header()] = None,
remote_user_perm: Annotated[str, Header()] = None,
) -> ImageGenerationConfig:
"""
Sets the complete image generation configuration.
"""
if not has_admin_rights(remote_user, remote_user_perm):
raise HTTPException(
status_code=401,
detail="You do not have permission to modify tool settings.",
)

# If enabling and selecting a tool, validate that the tool exists
if config.enabled and config.selected_tool is not None:
available_tools = get_image_generation_tool_metadata()
available_tool_names = [tool.name for tool in available_tools]

if config.selected_tool not in available_tool_names:
raise HTTPException(
status_code=400,
detail=f"Invalid tool selection. Available tools: {available_tool_names}",
)

set_image_generation_config(config)
return config


@router.post(
Expand Down Expand Up @@ -169,6 +355,16 @@ def delete_tool(

decoded_name = unquote(name)

# Prevent deletion of image generation tools
available_image_tools = get_image_generation_tool_metadata()
image_tool_names = [tool.name for tool in available_image_tools]

if decoded_name in image_tool_names:
raise HTTPException(
status_code=400,
detail="Image generation tools cannot be deleted.",
)

mcp_config = get_mcp_config()

# Find the tool with the given name
Expand Down
5 changes: 5 additions & 0 deletions llm-service/app/services/chat/chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -156,6 +156,11 @@ def finalize_response(
evaluations.append(Evaluation(name="relevance", value=relevance))
evaluations.append(Evaluation(name="faithfulness", value=faithfulness))
response_source_nodes = format_source_nodes(chat_response)

# remove the sandbox prefix from the response for OpenAI image generation responses
if "(sandbox:" in chat_response.response:
chat_response.response = chat_response.response.replace("(sandbox:", "(")

new_chat_message = RagStudioChatMessage(
id=response_id,
session_id=session.id,
Expand Down
27 changes: 21 additions & 6 deletions llm-service/app/services/chat/streaming_chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,11 @@
import uuid
from typing import Optional, Generator

from llama_index.core.base.llms.types import ChatResponse, ChatMessage
from llama_index.core.base.llms.types import (
ChatResponse,
ChatMessage,
TextBlock,
)
from llama_index.core.chat_engine.types import (
AgentChatResponse,
StreamingAgentChatResponse,
Expand Down Expand Up @@ -95,7 +99,6 @@ def stream_chat(
if not query_configuration.use_tool_calling and (
len(session.get_all_data_source_ids()) == 0 or total_data_sources_size == 0
):
# put a poison pill in the queue to stop the tool events stream
return _stream_direct_llm_chat(session, response_id, query, user_name)

condensed_question, streaming_chat_response = build_streamer(
Expand All @@ -122,7 +125,9 @@ def _run_streaming_chat(
streaming_chat_response: StreamingAgentChatResponse,
condensed_question: Optional[str] = None,
) -> Generator[ChatResponse, None, None]:
response: ChatResponse = ChatResponse(message=ChatMessage(content=query))
response: ChatResponse = ChatResponse(
message=ChatMessage(blocks=[TextBlock(text=query)])
)
if streaming_chat_response.chat_stream:
for response in streaming_chat_response.chat_stream:
response.additional_kwargs["response_id"] = response_id
Expand Down Expand Up @@ -162,7 +167,9 @@ def build_streamer(
chat_history = retrieve_chat_history(session.id)
chat_messages = list(
map(
lambda message: ChatMessage(role=message.role, content=message.content),
lambda message: ChatMessage(
role=message.role, blocks=[TextBlock(text=message.content)]
),
chat_history,
)
)
Expand Down Expand Up @@ -190,9 +197,17 @@ def _stream_direct_llm_chat(
record_direct_llm_mlflow_run(response_id, session, user_name)

chat_response = llm_completion.stream_completion(
session.id, query, session.inference_model
session.id,
ChatMessage(
blocks=[
TextBlock(text=query),
]
),
session.inference_model,
)
response: ChatResponse = ChatResponse(
message=ChatMessage(blocks=[TextBlock(text=query)])
)
response: ChatResponse = ChatResponse(message=ChatMessage(content=query))
for response in chat_response:
response.additional_kwargs["response_id"] = response_id
yield response
Expand Down
Loading
Loading