contentgrid-assistant-api 0.0.2__tar.gz → 0.0.4__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {contentgrid_assistant_api-0.0.2/src/contentgrid_assistant_api.egg-info → contentgrid_assistant_api-0.0.4}/PKG-INFO +2 -1
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/pyproject.toml +1 -0
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/src/contentgrid_assistant_api/app.py +18 -6
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/src/contentgrid_assistant_api/config.py +23 -0
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/src/contentgrid_assistant_api/routers/agent_home.py +23 -7
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/src/contentgrid_assistant_api/routers/message_router.py +28 -5
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/src/contentgrid_assistant_api/routers/thread_router.py +29 -4
- contentgrid_assistant_api-0.0.4/src/contentgrid_assistant_api/tracing.py +67 -0
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/src/contentgrid_assistant_api/types/agents.py +5 -4
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4/src/contentgrid_assistant_api.egg-info}/PKG-INFO +2 -1
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/src/contentgrid_assistant_api.egg-info/SOURCES.txt +3 -0
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/src/contentgrid_assistant_api.egg-info/requires.txt +1 -0
- contentgrid_assistant_api-0.0.4/src/contentgrid_assistant_api.egg-info/scm_file_list.json +24 -0
- contentgrid_assistant_api-0.0.4/src/contentgrid_assistant_api.egg-info/scm_version.json +8 -0
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/LICENSE +0 -0
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/README.md +0 -0
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/pytest.ini +0 -0
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/setup.cfg +0 -0
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/setup.py +0 -0
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/src/contentgrid_assistant_api/db/repositories/thread_repository.py +0 -0
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/src/contentgrid_assistant_api/db/types/message.py +0 -0
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/src/contentgrid_assistant_api/db/types/thread.py +0 -0
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/src/contentgrid_assistant_api/dependencies.py +0 -0
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/src/contentgrid_assistant_api/types/context.py +0 -0
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/src/contentgrid_assistant_api.egg-info/dependency_links.txt +0 -0
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/src/contentgrid_assistant_api.egg-info/top_level.txt +0 -0
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/tests/mock_agent.py +0 -0
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/tests/test_thread_access_control.py +0 -0
- {contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/tests/test_tools_endpoint.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: contentgrid-assistant-api
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: FastAPI wrapper for contentgrid assistants
|
|
5
5
|
Author-email: Ranec Belpaire <ranec.belpaire@xenit.eu>
|
|
6
6
|
License: Copyright 2024 Xenit Solutions
|
|
@@ -30,6 +30,7 @@ Requires-Dist: langchain>=1.2.3
|
|
|
30
30
|
Requires-Dist: langchain_openai>=1.1.7
|
|
31
31
|
Requires-Dist: langgraph>=1.0.5
|
|
32
32
|
Requires-Dist: langgraph-checkpoint-postgres>=3.0.2
|
|
33
|
+
Requires-Dist: langfuse>=3.0.0
|
|
33
34
|
Requires-Dist: python-dotenv>=1.1.1
|
|
34
35
|
Requires-Dist: requests<3,>=2.32.5
|
|
35
36
|
Requires-Dist: types-requests>=2.32.4
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import logging
|
|
2
|
-
from
|
|
2
|
+
from contextlib import asynccontextmanager
|
|
3
|
+
from typing import List, Optional
|
|
3
4
|
from fastapi import Depends, FastAPI, status, Request
|
|
4
5
|
from fastapi.middleware.cors import CORSMiddleware
|
|
5
6
|
from openai import APIError
|
|
@@ -9,8 +10,10 @@ from contentgrid_extension_helpers.logging import setup_json_logging
|
|
|
9
10
|
from contentgrid_extension_helpers.problem_response import ProblemResponse
|
|
10
11
|
|
|
11
12
|
from contentgrid_assistant_api.routers.agent_home import generate_agent_home_router
|
|
12
|
-
from contentgrid_assistant_api.config import AssistantExtensionConfig, DatabaseConfig
|
|
13
|
+
from contentgrid_assistant_api.config import AssistantExtensionConfig, DatabaseConfig, LangfuseConfig
|
|
13
14
|
from contentgrid_assistant_api.types.agents import Agent, AgentHomeResponse
|
|
15
|
+
from contentgrid_assistant_api.tracing import setup_langfuse, shutdown_langfuse
|
|
16
|
+
from pydantic import HttpUrl
|
|
14
17
|
|
|
15
18
|
|
|
16
19
|
class ContentGridAssistantAPI(FastAPI):
|
|
@@ -18,14 +21,16 @@ class ContentGridAssistantAPI(FastAPI):
|
|
|
18
21
|
|
|
19
22
|
def __init__(self,
|
|
20
23
|
extension_config: AssistantExtensionConfig | None = None,
|
|
21
|
-
database_config: DatabaseConfig | None = None,
|
|
24
|
+
database_config: DatabaseConfig | None = None,
|
|
25
|
+
langfuse_config: LangfuseConfig | None = None,
|
|
22
26
|
agents: List[Agent] = [],
|
|
23
27
|
*args, **kwargs):
|
|
24
|
-
super().__init__(*args, **kwargs)
|
|
25
28
|
self.extension_config = extension_config or AssistantExtensionConfig()
|
|
26
29
|
self.database_config = database_config or DatabaseConfig()
|
|
30
|
+
self.langfuse_config = langfuse_config or LangfuseConfig()
|
|
27
31
|
|
|
28
32
|
self._setup_logging()
|
|
33
|
+
super().__init__(*args, lifespan=self._lifespan, **kwargs)
|
|
29
34
|
if not self.extension_config.production:
|
|
30
35
|
self._setup_cors()
|
|
31
36
|
self._setup_hal_response()
|
|
@@ -54,6 +59,13 @@ class ContentGridAssistantAPI(FastAPI):
|
|
|
54
59
|
if self.extension_config.production:
|
|
55
60
|
setup_json_logging()
|
|
56
61
|
|
|
62
|
+
@asynccontextmanager
|
|
63
|
+
async def _lifespan(self, app: "ContentGridAssistantAPI"):
|
|
64
|
+
"""Manage application lifespan: initialize and shut down Langfuse."""
|
|
65
|
+
setup_langfuse(self.langfuse_config)
|
|
66
|
+
yield
|
|
67
|
+
shutdown_langfuse(self.langfuse_config)
|
|
68
|
+
|
|
57
69
|
def _setup_cors(self):
|
|
58
70
|
"""Configure CORS middleware"""
|
|
59
71
|
self.add_middleware(
|
|
@@ -89,11 +101,11 @@ class ContentGridAssistantAPI(FastAPI):
|
|
|
89
101
|
return "ok"
|
|
90
102
|
|
|
91
103
|
@self.get(f"{self._server_prefix}/", response_model=FastAPIHALCollection, response_model_exclude_unset=True)
|
|
92
|
-
def get_server_resources():
|
|
104
|
+
def get_server_resources(origin: Optional[HttpUrl] = None):
|
|
93
105
|
return FastAPIHALCollection(
|
|
94
106
|
_embedded={
|
|
95
107
|
"agents" : [
|
|
96
|
-
AgentHomeResponse(**agent.model_dump(), tags=[agent.name]) for agent in agents
|
|
108
|
+
AgentHomeResponse(**agent.model_dump(), origin=origin, tags=[agent.name]) for agent in agents
|
|
97
109
|
]
|
|
98
110
|
},
|
|
99
111
|
_links={
|
|
@@ -1,9 +1,32 @@
|
|
|
1
1
|
from pydantic_settings import BaseSettings
|
|
2
2
|
from pydantic import Field, ConfigDict
|
|
3
3
|
from pydantic import computed_field
|
|
4
|
+
from typing import Optional
|
|
4
5
|
import logging
|
|
5
6
|
import urllib
|
|
6
7
|
|
|
8
|
+
class LangfuseConfig(BaseSettings):
|
|
9
|
+
"""Configuration for Langfuse observability.
|
|
10
|
+
|
|
11
|
+
Set these environment variables to enable tracing:
|
|
12
|
+
- LANGFUSE_SECRET_KEY: Your Langfuse secret key (sk-lf-...)
|
|
13
|
+
- LANGFUSE_PUBLIC_KEY: Your Langfuse public key (pk-lf-...)
|
|
14
|
+
- LANGFUSE_BASE_URL: Langfuse server URL (default: https://cloud.langfuse.com)
|
|
15
|
+
"""
|
|
16
|
+
model_config = ConfigDict(extra="allow", env_file=[".env", ".env.secret"], env_file_encoding="utf-8")
|
|
17
|
+
|
|
18
|
+
langfuse_secret_key: Optional[str] = Field(default=None)
|
|
19
|
+
langfuse_public_key: Optional[str] = Field(default=None)
|
|
20
|
+
langfuse_base_url: str = Field(default="https://cloud.langfuse.com")
|
|
21
|
+
langfuse_enabled: bool = Field(default=True, description="Enable/disable Langfuse tracing")
|
|
22
|
+
|
|
23
|
+
@computed_field
|
|
24
|
+
@property
|
|
25
|
+
def is_configured(self) -> bool:
|
|
26
|
+
"""Check if Langfuse credentials are configured"""
|
|
27
|
+
return bool(self.langfuse_secret_key and self.langfuse_public_key and self.langfuse_enabled)
|
|
28
|
+
|
|
29
|
+
|
|
7
30
|
class AssistantExtensionConfig(BaseSettings):
|
|
8
31
|
model_config = ConfigDict(extra="allow", env_file=[".env", ".env.secret"], env_file_encoding="utf-8")
|
|
9
32
|
|
|
@@ -1,10 +1,13 @@
|
|
|
1
|
+
from typing import Optional
|
|
2
|
+
|
|
1
3
|
from fastapi import APIRouter, FastAPI
|
|
2
4
|
import os
|
|
3
5
|
from fastapi.concurrency import asynccontextmanager
|
|
4
|
-
from contentgrid_assistant_api.config import DatabaseConfig, AssistantExtensionConfig
|
|
6
|
+
from contentgrid_assistant_api.config import DatabaseConfig, AssistantExtensionConfig, LangfuseConfig
|
|
5
7
|
from contentgrid_assistant_api.dependencies import DependencyResolver
|
|
6
|
-
from contentgrid_assistant_api.routers.thread_router import generate_agent_thread_router
|
|
8
|
+
from contentgrid_assistant_api.routers.thread_router import HALLinkFor, generate_agent_thread_router
|
|
7
9
|
from contentgrid_assistant_api.types.agents import Agent, AgentHomeResponse
|
|
10
|
+
from pydantic import HttpUrl
|
|
8
11
|
|
|
9
12
|
def exit_uvicorn():
|
|
10
13
|
import signal
|
|
@@ -13,10 +16,18 @@ def exit_uvicorn():
|
|
|
13
16
|
os.kill(os.getppid(), signal.SIGINT)
|
|
14
17
|
|
|
15
18
|
|
|
16
|
-
def generate_agent_home_router(
|
|
19
|
+
def generate_agent_home_router(
|
|
20
|
+
agent: Agent,
|
|
21
|
+
extension_config: AssistantExtensionConfig,
|
|
22
|
+
database_config: DatabaseConfig,
|
|
23
|
+
langfuse_config: LangfuseConfig | None = None
|
|
24
|
+
) -> APIRouter:
|
|
17
25
|
if database_config.pg_dbname == database_config.__class__.model_fields["pg_dbname"].default:
|
|
18
26
|
# Check if the pg_dbname is still the default, if yes use the agent name
|
|
19
27
|
database_config.pg_dbname = agent.name
|
|
28
|
+
|
|
29
|
+
# Use provided langfuse_config or create default
|
|
30
|
+
langfuse_cfg = langfuse_config or LangfuseConfig()
|
|
20
31
|
|
|
21
32
|
dep_resolver = DependencyResolver(agent=agent, db_config=database_config)
|
|
22
33
|
|
|
@@ -32,10 +43,15 @@ def generate_agent_home_router(agent : Agent, extension_config: AssistantExtensi
|
|
|
32
43
|
|
|
33
44
|
|
|
34
45
|
router = APIRouter(lifespan=lifespan, tags=[agent.name])
|
|
35
|
-
router.include_router(generate_agent_thread_router(
|
|
46
|
+
router.include_router(generate_agent_thread_router(
|
|
47
|
+
dep_resolver,
|
|
48
|
+
extension_config,
|
|
49
|
+
langfuse_config=langfuse_cfg,
|
|
50
|
+
agent_name=agent.name,
|
|
51
|
+
tags=[agent.name]
|
|
52
|
+
))
|
|
36
53
|
|
|
37
54
|
@router.get("/", response_model=AgentHomeResponse, response_model_exclude_unset=True)
|
|
38
|
-
def get_agent_home():
|
|
39
|
-
return AgentHomeResponse(**agent.model_dump(), tags=[agent.name])
|
|
40
|
-
|
|
55
|
+
def get_agent_home(origin: Optional[HttpUrl] = None):
|
|
56
|
+
return AgentHomeResponse(**agent.model_dump(), origin=origin, tags=[agent.name])
|
|
41
57
|
return router
|
|
@@ -6,10 +6,11 @@ from typing import Annotated, AsyncGenerator, List, Union, Optional
|
|
|
6
6
|
import typing
|
|
7
7
|
import uuid
|
|
8
8
|
from enum import Enum
|
|
9
|
+
from contentgrid_assistant_api.tracing import _create_langfuse_config
|
|
9
10
|
from langgraph.graph.state import CompiledStateGraph
|
|
10
11
|
from fastapi import APIRouter, BackgroundTasks, File, Form, Request, UploadFile, status
|
|
11
12
|
from fastapi.params import Depends
|
|
12
|
-
from contentgrid_assistant_api.config import AssistantExtensionConfig
|
|
13
|
+
from contentgrid_assistant_api.config import AssistantExtensionConfig, LangfuseConfig
|
|
13
14
|
from fastapi.responses import StreamingResponse
|
|
14
15
|
from contentgrid_assistant_api.db.repositories.thread_repository import ThreadRepository
|
|
15
16
|
from contentgrid_assistant_api.db.types.message import HALHumanMessage, HALAIMessage, HALSystemMessage, HALToolMessage
|
|
@@ -20,14 +21,20 @@ from contentgrid_extension_helpers.responses.hal import FastAPIHALCollection, HA
|
|
|
20
21
|
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage, BaseMessage, ToolMessage
|
|
21
22
|
from fastapi import HTTPException
|
|
22
23
|
|
|
23
|
-
|
|
24
24
|
# ContentBlocks
|
|
25
25
|
from langchain_core.messages.content import create_image_block, create_file_block, create_audio_block, create_text_block, ImageContentBlock, AudioContentBlock, FileContentBlock, TextContentBlock
|
|
26
26
|
import logging
|
|
27
27
|
|
|
28
28
|
from contentgrid_assistant_api.types.context import DefaultThreadContext
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
|
|
31
|
+
def generate_agent_message_router(
|
|
32
|
+
dep_resolver: DependencyResolver,
|
|
33
|
+
extension_config: AssistantExtensionConfig,
|
|
34
|
+
langfuse_config: LangfuseConfig,
|
|
35
|
+
agent_name: str,
|
|
36
|
+
tags: Optional[List[str | Enum]] = None
|
|
37
|
+
) -> APIRouter:
|
|
31
38
|
messagesrouter = APIRouter(prefix="/{thread_id}" + extension_config.routes_message_prefix, tags=tags or ["messages"])
|
|
32
39
|
|
|
33
40
|
def convert_to_hal_message(message: BaseMessage, thread_id: uuid.UUID) -> Union[HALHumanMessage, HALAIMessage, HALSystemMessage, HALToolMessage]:
|
|
@@ -160,6 +167,9 @@ def generate_agent_message_router(dep_resolver: DependencyResolver, extension_co
|
|
|
160
167
|
new_message = HumanMessage(content=content_blocks) # type: ignore
|
|
161
168
|
messages = [new_message]
|
|
162
169
|
|
|
170
|
+
# Create Langfuse config with session_id, user_id, and tags for observability
|
|
171
|
+
lf_config = _create_langfuse_config(thread_context, user, agent_name, langfuse_config)
|
|
172
|
+
|
|
163
173
|
if streaming:
|
|
164
174
|
state = agent.get_state(config={"configurable" : thread_context}) #type: ignore
|
|
165
175
|
nb_current_messages = 0
|
|
@@ -168,9 +178,15 @@ def generate_agent_message_router(dep_resolver: DependencyResolver, extension_co
|
|
|
168
178
|
|
|
169
179
|
@typing.no_type_check
|
|
170
180
|
async def generate_stream(current_message_index) -> AsyncGenerator[str, None]:
|
|
181
|
+
# Merge Langfuse config with LangGraph config
|
|
182
|
+
stream_config = {
|
|
183
|
+
"configurable": thread_context,
|
|
184
|
+
"recursion_limit": extension_config.graph_recursion_limit,
|
|
185
|
+
**lf_config
|
|
186
|
+
}
|
|
171
187
|
for mode, chunk in agent.stream(
|
|
172
188
|
{"messages": messages},
|
|
173
|
-
|
|
189
|
+
stream_config,
|
|
174
190
|
context=thread_context,
|
|
175
191
|
stream_mode=["values", "messages"]
|
|
176
192
|
):
|
|
@@ -214,11 +230,18 @@ def generate_agent_message_router(dep_resolver: DependencyResolver, extension_co
|
|
|
214
230
|
}
|
|
215
231
|
)
|
|
216
232
|
else:
|
|
233
|
+
# Capture lf_config for closure
|
|
234
|
+
invoke_config = {
|
|
235
|
+
"configurable": thread_context,
|
|
236
|
+
"recursion_limit": extension_config.graph_recursion_limit,
|
|
237
|
+
**lf_config
|
|
238
|
+
}
|
|
239
|
+
|
|
217
240
|
def process_agent_response():
|
|
218
241
|
try:
|
|
219
242
|
agent.invoke(
|
|
220
243
|
{"messages": messages},
|
|
221
|
-
|
|
244
|
+
invoke_config,
|
|
222
245
|
context=thread_context,
|
|
223
246
|
)
|
|
224
247
|
except Exception as e:
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
from enum import Enum
|
|
3
3
|
from typing import Annotated, List, Optional
|
|
4
4
|
import uuid
|
|
5
|
+
from contentgrid_assistant_api.tracing import _create_langfuse_config_for_thread
|
|
5
6
|
from fastapi import APIRouter, Depends, Query, status
|
|
6
7
|
from pydantic import HttpUrl
|
|
7
8
|
|
|
@@ -15,13 +16,29 @@ from contentgrid_assistant_api.types.agents import AgentToolCollectionResponse,
|
|
|
15
16
|
from contentgrid_assistant_api.types.context import DefaultThreadContext
|
|
16
17
|
from langchain.messages import HumanMessage
|
|
17
18
|
from langgraph.graph.state import CompiledStateGraph
|
|
18
|
-
from contentgrid_assistant_api.config import AssistantExtensionConfig
|
|
19
|
+
from contentgrid_assistant_api.config import AssistantExtensionConfig, LangfuseConfig
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
def generate_agent_thread_router(
|
|
23
|
+
dep_resolver: DependencyResolver,
|
|
24
|
+
extension_config: AssistantExtensionConfig,
|
|
25
|
+
langfuse_config: LangfuseConfig | None = None,
|
|
26
|
+
agent_name: str = "agent",
|
|
27
|
+
tags: Optional[List[str | Enum]] = None
|
|
28
|
+
):
|
|
29
|
+
# Use provided langfuse_config or create default
|
|
30
|
+
langfuse_cfg = langfuse_config or LangfuseConfig()
|
|
19
31
|
|
|
20
|
-
def generate_agent_thread_router(dep_resolver: DependencyResolver, extension_config: AssistantExtensionConfig, tags: Optional[List[str | Enum]]=None):
|
|
21
32
|
threadrouter = APIRouter(prefix=extension_config.routes_thread_prefix, tags=tags or ["threads"])
|
|
22
33
|
|
|
23
34
|
threadrouter.include_router(
|
|
24
|
-
generate_agent_message_router(
|
|
35
|
+
generate_agent_message_router(
|
|
36
|
+
dep_resolver,
|
|
37
|
+
extension_config,
|
|
38
|
+
langfuse_config=langfuse_cfg,
|
|
39
|
+
agent_name=agent_name,
|
|
40
|
+
tags=tags
|
|
41
|
+
)
|
|
25
42
|
)
|
|
26
43
|
|
|
27
44
|
@threadrouter.post("/", response_model=ThreadRead, status_code=status.HTTP_201_CREATED, response_model_exclude_none=True)
|
|
@@ -36,9 +53,17 @@ def generate_agent_thread_router(dep_resolver: DependencyResolver, extension_con
|
|
|
36
53
|
thread_id = uuid.uuid4()
|
|
37
54
|
messages = [HumanMessage(content=extension_config.opening_message)]
|
|
38
55
|
context = dep_resolver.agent.thread_context(thread_id=str(thread_id), user=user, origin=origin)
|
|
56
|
+
|
|
57
|
+
# Create Langfuse config for thread creation tracing
|
|
58
|
+
lf_config = _create_langfuse_config_for_thread(str(thread_id), user, agent_name, langfuse_cfg, extra_tags=["create-thread"])
|
|
59
|
+
invoke_config = {
|
|
60
|
+
"configurable": context,
|
|
61
|
+
**lf_config
|
|
62
|
+
}
|
|
63
|
+
|
|
39
64
|
agent.invoke(
|
|
40
65
|
{"messages": messages},
|
|
41
|
-
|
|
66
|
+
invoke_config, #type: ignore
|
|
42
67
|
context=context, #type: ignore
|
|
43
68
|
)
|
|
44
69
|
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
|
|
2
|
+
import logging
|
|
3
|
+
from contentgrid_assistant_api.config import LangfuseConfig
|
|
4
|
+
from contentgrid_assistant_api.types.context import ContentGridUser, DefaultThreadContext
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def setup_langfuse(langfuse_config: LangfuseConfig) -> None:
|
|
8
|
+
"""Initialize the Langfuse observability client."""
|
|
9
|
+
if langfuse_config.is_configured:
|
|
10
|
+
from langfuse import Langfuse
|
|
11
|
+
Langfuse(
|
|
12
|
+
public_key=langfuse_config.langfuse_public_key,
|
|
13
|
+
secret_key=langfuse_config.langfuse_secret_key,
|
|
14
|
+
base_url=langfuse_config.langfuse_base_url,
|
|
15
|
+
)
|
|
16
|
+
logging.info("Langfuse observability initialized successfully")
|
|
17
|
+
else:
|
|
18
|
+
logging.info("Langfuse not configured - tracing disabled. Set LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY to enable.")
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def shutdown_langfuse(langfuse_config: LangfuseConfig) -> None:
|
|
22
|
+
"""Flush and shut down the Langfuse client."""
|
|
23
|
+
if langfuse_config.is_configured:
|
|
24
|
+
from langfuse import get_client
|
|
25
|
+
get_client().shutdown()
|
|
26
|
+
logging.info("Langfuse client shutdown complete")
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def _create_langfuse_config_for_thread(
|
|
30
|
+
thread_id: str,
|
|
31
|
+
user: ContentGridUser,
|
|
32
|
+
agent_name: str,
|
|
33
|
+
langfuse_config: LangfuseConfig,
|
|
34
|
+
extra_tags: list[str] | None = None
|
|
35
|
+
) -> dict:
|
|
36
|
+
"""Create LangChain config with Langfuse callback handler and metadata.
|
|
37
|
+
|
|
38
|
+
Per Langfuse best practices:
|
|
39
|
+
- session_id: Groups conversation messages together (using thread_id)
|
|
40
|
+
- user_id: Enables user filtering and cost attribution
|
|
41
|
+
- tags: Per-feature analytics (using agent name)
|
|
42
|
+
"""
|
|
43
|
+
config: dict = {}
|
|
44
|
+
|
|
45
|
+
if langfuse_config.is_configured:
|
|
46
|
+
try:
|
|
47
|
+
from langfuse.langchain import CallbackHandler
|
|
48
|
+
langfuse_handler = CallbackHandler()
|
|
49
|
+
config["callbacks"] = [langfuse_handler]
|
|
50
|
+
config["metadata"] = {
|
|
51
|
+
"langfuse_session_id": thread_id,
|
|
52
|
+
"langfuse_user_id": user.sub,
|
|
53
|
+
"langfuse_tags": [agent_name] + (extra_tags or [])
|
|
54
|
+
}
|
|
55
|
+
except ImportError:
|
|
56
|
+
logging.warning("Langfuse not installed, tracing disabled")
|
|
57
|
+
|
|
58
|
+
return config
|
|
59
|
+
|
|
60
|
+
def _create_langfuse_config(
|
|
61
|
+
thread_context: DefaultThreadContext,
|
|
62
|
+
user: ContentGridUser,
|
|
63
|
+
agent_name: str,
|
|
64
|
+
langfuse_config: LangfuseConfig
|
|
65
|
+
) -> dict:
|
|
66
|
+
"""Create LangChain config with Langfuse callback handler for an existing thread."""
|
|
67
|
+
return _create_langfuse_config_for_thread(thread_context.thread_id, user, agent_name, langfuse_config)
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
from typing import Any, Callable, List, Optional
|
|
3
3
|
from enum import Enum
|
|
4
4
|
import uuid
|
|
5
|
-
from pydantic import BaseModel, Field, ConfigDict
|
|
5
|
+
from pydantic import BaseModel, Field, ConfigDict, HttpUrl
|
|
6
6
|
from langchain.tools import BaseTool
|
|
7
7
|
from contentgrid_extension_helpers.responses.hal import FastAPIHALResponse, FastAPIHALCollection, HALLinkFor
|
|
8
8
|
from contentgrid_assistant_api.types.context import DefaultThreadContext
|
|
@@ -25,11 +25,12 @@ class AgentHomeResponse(FastAPIHALResponse):
|
|
|
25
25
|
name : str
|
|
26
26
|
version : str
|
|
27
27
|
|
|
28
|
-
def __init__(self, tags: Optional[List[str | Enum]]=None, **kwargs):
|
|
28
|
+
def __init__(self, origin: Optional[HttpUrl] = None, tags: Optional[List[str | Enum]] = None, **kwargs):
|
|
29
29
|
super().__init__(**kwargs)
|
|
30
|
+
params = {"origin": str(origin)} if origin else {}
|
|
30
31
|
self.links = {
|
|
31
|
-
"self"
|
|
32
|
-
"threads"
|
|
32
|
+
"self": HALLinkFor(endpoint_function_name="get_agent_home", params=params, tags=tags),
|
|
33
|
+
"threads": HALLinkFor(endpoint_function_name="read_threads", params=params, tags=tags),
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
class AgentToolResponse(FastAPIHALResponse):
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: contentgrid-assistant-api
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: FastAPI wrapper for contentgrid assistants
|
|
5
5
|
Author-email: Ranec Belpaire <ranec.belpaire@xenit.eu>
|
|
6
6
|
License: Copyright 2024 Xenit Solutions
|
|
@@ -30,6 +30,7 @@ Requires-Dist: langchain>=1.2.3
|
|
|
30
30
|
Requires-Dist: langchain_openai>=1.1.7
|
|
31
31
|
Requires-Dist: langgraph>=1.0.5
|
|
32
32
|
Requires-Dist: langgraph-checkpoint-postgres>=3.0.2
|
|
33
|
+
Requires-Dist: langfuse>=3.0.0
|
|
33
34
|
Requires-Dist: python-dotenv>=1.1.1
|
|
34
35
|
Requires-Dist: requests<3,>=2.32.5
|
|
35
36
|
Requires-Dist: types-requests>=2.32.4
|
|
@@ -6,10 +6,13 @@ setup.py
|
|
|
6
6
|
src/contentgrid_assistant_api/app.py
|
|
7
7
|
src/contentgrid_assistant_api/config.py
|
|
8
8
|
src/contentgrid_assistant_api/dependencies.py
|
|
9
|
+
src/contentgrid_assistant_api/tracing.py
|
|
9
10
|
src/contentgrid_assistant_api.egg-info/PKG-INFO
|
|
10
11
|
src/contentgrid_assistant_api.egg-info/SOURCES.txt
|
|
11
12
|
src/contentgrid_assistant_api.egg-info/dependency_links.txt
|
|
12
13
|
src/contentgrid_assistant_api.egg-info/requires.txt
|
|
14
|
+
src/contentgrid_assistant_api.egg-info/scm_file_list.json
|
|
15
|
+
src/contentgrid_assistant_api.egg-info/scm_version.json
|
|
13
16
|
src/contentgrid_assistant_api.egg-info/top_level.txt
|
|
14
17
|
src/contentgrid_assistant_api/db/repositories/thread_repository.py
|
|
15
18
|
src/contentgrid_assistant_api/db/types/message.py
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"files": [
|
|
3
|
+
"README.md",
|
|
4
|
+
"LICENSE",
|
|
5
|
+
"setup.py",
|
|
6
|
+
"pyproject.toml",
|
|
7
|
+
"pytest.ini",
|
|
8
|
+
"src/contentgrid_assistant_api/dependencies.py",
|
|
9
|
+
"src/contentgrid_assistant_api/config.py",
|
|
10
|
+
"src/contentgrid_assistant_api/app.py",
|
|
11
|
+
"src/contentgrid_assistant_api/tracing.py",
|
|
12
|
+
"src/contentgrid_assistant_api/db/repositories/thread_repository.py",
|
|
13
|
+
"src/contentgrid_assistant_api/db/types/message.py",
|
|
14
|
+
"src/contentgrid_assistant_api/db/types/thread.py",
|
|
15
|
+
"src/contentgrid_assistant_api/types/context.py",
|
|
16
|
+
"src/contentgrid_assistant_api/types/agents.py",
|
|
17
|
+
"src/contentgrid_assistant_api/routers/agent_home.py",
|
|
18
|
+
"src/contentgrid_assistant_api/routers/message_router.py",
|
|
19
|
+
"src/contentgrid_assistant_api/routers/thread_router.py",
|
|
20
|
+
"tests/test_tools_endpoint.py",
|
|
21
|
+
"tests/test_thread_access_control.py",
|
|
22
|
+
"tests/mock_agent.py"
|
|
23
|
+
]
|
|
24
|
+
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{contentgrid_assistant_api-0.0.2 → contentgrid_assistant_api-0.0.4}/tests/test_tools_endpoint.py
RENAMED
|
File without changes
|