mirror of
https://github.com/Significant-Gravitas/AutoGPT.git
synced 2026-01-23 05:57:58 -05:00
Compare commits
1 Commits
dev
...
swiftyos/p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
76c751bd56 |
@@ -48,6 +48,7 @@ from .response_model import (
|
||||
StreamUsage,
|
||||
)
|
||||
from .tools import execute_tool, tools
|
||||
from .tracking import track_user_message
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
@@ -103,16 +104,33 @@ async def _build_system_prompt(user_id: str | None) -> tuple[str, Any]:
|
||||
return compiled, understanding
|
||||
|
||||
|
||||
async def _generate_session_title(message: str) -> str | None:
|
||||
async def _generate_session_title(
|
||||
message: str,
|
||||
user_id: str | None = None,
|
||||
session_id: str | None = None,
|
||||
) -> str | None:
|
||||
"""Generate a concise title for a chat session based on the first message.
|
||||
|
||||
Args:
|
||||
message: The first user message in the session
|
||||
user_id: User ID for OpenRouter tracing (optional)
|
||||
session_id: Session ID for OpenRouter tracing (optional)
|
||||
|
||||
Returns:
|
||||
A short title (3-6 words) or None if generation fails
|
||||
"""
|
||||
try:
|
||||
# Build extra_body for OpenRouter tracing and PostHog analytics
|
||||
extra_body: dict[str, Any] = {}
|
||||
if user_id:
|
||||
extra_body["user"] = user_id[:128] # OpenRouter limit
|
||||
extra_body["posthogDistinctId"] = user_id
|
||||
if session_id:
|
||||
extra_body["session_id"] = session_id[:128] # OpenRouter limit
|
||||
extra_body["posthogProperties"] = {
|
||||
"environment": settings.config.app_env.value,
|
||||
}
|
||||
|
||||
response = await client.chat.completions.create(
|
||||
model=config.title_model,
|
||||
messages=[
|
||||
@@ -127,6 +145,7 @@ async def _generate_session_title(message: str) -> str | None:
|
||||
{"role": "user", "content": message[:500]}, # Limit input length
|
||||
],
|
||||
max_tokens=20,
|
||||
extra_body=extra_body,
|
||||
)
|
||||
title = response.choices[0].message.content
|
||||
if title:
|
||||
@@ -237,6 +256,14 @@ async def stream_chat_completion(
|
||||
f"new message_count={len(session.messages)}"
|
||||
)
|
||||
|
||||
# Track user message in PostHog
|
||||
if is_user_message:
|
||||
track_user_message(
|
||||
user_id=user_id,
|
||||
session_id=session_id,
|
||||
message_length=len(message),
|
||||
)
|
||||
|
||||
logger.info(
|
||||
f"Upserting session: {session.session_id} with user id {session.user_id}, "
|
||||
f"message_count={len(session.messages)}"
|
||||
@@ -256,10 +283,15 @@ async def stream_chat_completion(
|
||||
# stale data issues when the main flow modifies the session
|
||||
captured_session_id = session_id
|
||||
captured_message = message
|
||||
captured_user_id = user_id
|
||||
|
||||
async def _update_title():
|
||||
try:
|
||||
title = await _generate_session_title(captured_message)
|
||||
title = await _generate_session_title(
|
||||
captured_message,
|
||||
user_id=captured_user_id,
|
||||
session_id=captured_session_id,
|
||||
)
|
||||
if title:
|
||||
# Use dedicated title update function that doesn't
|
||||
# touch messages, avoiding race conditions
|
||||
@@ -698,6 +730,20 @@ async def _stream_chat_chunks(
|
||||
f"{f' (retry {retry_count}/{MAX_RETRIES})' if retry_count > 0 else ''}"
|
||||
)
|
||||
|
||||
# Build extra_body for OpenRouter tracing and PostHog analytics
|
||||
extra_body: dict[str, Any] = {
|
||||
"posthogProperties": {
|
||||
"environment": settings.config.app_env.value,
|
||||
},
|
||||
}
|
||||
if session.user_id:
|
||||
extra_body["user"] = session.user_id[:128] # OpenRouter limit
|
||||
extra_body["posthogDistinctId"] = session.user_id
|
||||
if session.session_id:
|
||||
extra_body["session_id"] = session.session_id[
|
||||
:128
|
||||
] # OpenRouter limit
|
||||
|
||||
# Create the stream with proper types
|
||||
stream = await client.chat.completions.create(
|
||||
model=model,
|
||||
@@ -706,6 +752,7 @@ async def _stream_chat_chunks(
|
||||
tool_choice="auto",
|
||||
stream=True,
|
||||
stream_options={"include_usage": True},
|
||||
extra_body=extra_body,
|
||||
)
|
||||
|
||||
# Variables to accumulate tool calls
|
||||
|
||||
@@ -3,6 +3,7 @@ from typing import TYPE_CHECKING, Any
|
||||
from openai.types.chat import ChatCompletionToolParam
|
||||
|
||||
from backend.api.features.chat.model import ChatSession
|
||||
from backend.api.features.chat.tracking import track_tool_called
|
||||
|
||||
from .add_understanding import AddUnderstandingTool
|
||||
from .agent_output import AgentOutputTool
|
||||
@@ -56,4 +57,13 @@ async def execute_tool(
|
||||
tool = TOOL_REGISTRY.get(tool_name)
|
||||
if not tool:
|
||||
raise ValueError(f"Tool {tool_name} not found")
|
||||
|
||||
# Track tool call in PostHog
|
||||
track_tool_called(
|
||||
user_id=user_id,
|
||||
session_id=session.session_id,
|
||||
tool_name=tool_name,
|
||||
tool_call_id=tool_call_id,
|
||||
)
|
||||
|
||||
return await tool.execute(user_id, session, tool_call_id, **parameters)
|
||||
|
||||
@@ -8,6 +8,10 @@ from pydantic import BaseModel, Field, field_validator
|
||||
|
||||
from backend.api.features.chat.config import ChatConfig
|
||||
from backend.api.features.chat.model import ChatSession
|
||||
from backend.api.features.chat.tracking import (
|
||||
track_agent_run_success,
|
||||
track_agent_scheduled,
|
||||
)
|
||||
from backend.api.features.library import db as library_db
|
||||
from backend.data.graph import GraphModel
|
||||
from backend.data.model import CredentialsMetaInput
|
||||
@@ -453,6 +457,16 @@ class RunAgentTool(BaseTool):
|
||||
session.successful_agent_runs.get(library_agent.graph_id, 0) + 1
|
||||
)
|
||||
|
||||
# Track in PostHog
|
||||
track_agent_run_success(
|
||||
user_id=user_id,
|
||||
session_id=session_id,
|
||||
graph_id=library_agent.graph_id,
|
||||
graph_name=library_agent.name,
|
||||
execution_id=execution.id,
|
||||
library_agent_id=library_agent.id,
|
||||
)
|
||||
|
||||
library_agent_link = f"/library/agents/{library_agent.id}"
|
||||
return ExecutionStartedResponse(
|
||||
message=(
|
||||
@@ -534,6 +548,18 @@ class RunAgentTool(BaseTool):
|
||||
session.successful_agent_schedules.get(library_agent.graph_id, 0) + 1
|
||||
)
|
||||
|
||||
# Track in PostHog
|
||||
track_agent_scheduled(
|
||||
user_id=user_id,
|
||||
session_id=session_id,
|
||||
graph_id=library_agent.graph_id,
|
||||
graph_name=library_agent.name,
|
||||
schedule_id=result.id,
|
||||
schedule_name=schedule_name,
|
||||
cron=cron,
|
||||
library_agent_id=library_agent.id,
|
||||
)
|
||||
|
||||
library_agent_link = f"/library/agents/{library_agent.id}"
|
||||
return ExecutionStartedResponse(
|
||||
message=(
|
||||
|
||||
229
autogpt_platform/backend/backend/api/features/chat/tracking.py
Normal file
229
autogpt_platform/backend/backend/api/features/chat/tracking.py
Normal file
@@ -0,0 +1,229 @@
|
||||
"""PostHog analytics tracking for the chat system."""
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
from posthog import Posthog
|
||||
|
||||
from backend.util.settings import Settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
settings = Settings()
|
||||
|
||||
# PostHog client instance (lazily initialized)
|
||||
_posthog_client: Posthog | None = None
|
||||
|
||||
|
||||
def _get_posthog_client() -> Posthog | None:
|
||||
"""Get or create the PostHog client instance."""
|
||||
global _posthog_client
|
||||
if _posthog_client is not None:
|
||||
return _posthog_client
|
||||
|
||||
if not settings.secrets.posthog_api_key:
|
||||
return None
|
||||
|
||||
_posthog_client = Posthog(
|
||||
settings.secrets.posthog_api_key,
|
||||
host=settings.secrets.posthog_host,
|
||||
)
|
||||
return _posthog_client
|
||||
|
||||
|
||||
def _get_base_properties() -> dict[str, Any]:
|
||||
"""Get base properties included in all events."""
|
||||
return {
|
||||
"environment": settings.config.app_env.value,
|
||||
"source": "chat_copilot",
|
||||
}
|
||||
|
||||
|
||||
def track_user_message(
|
||||
user_id: str | None,
|
||||
session_id: str,
|
||||
message_length: int,
|
||||
) -> None:
|
||||
"""Track when a user sends a message in chat.
|
||||
|
||||
Args:
|
||||
user_id: The user's ID (or None for anonymous)
|
||||
session_id: The chat session ID
|
||||
message_length: Length of the user's message
|
||||
"""
|
||||
client = _get_posthog_client()
|
||||
if not client:
|
||||
return
|
||||
|
||||
try:
|
||||
properties = {
|
||||
**_get_base_properties(),
|
||||
"session_id": session_id,
|
||||
"message_length": message_length,
|
||||
}
|
||||
client.capture(
|
||||
distinct_id=user_id or f"anonymous_{session_id}",
|
||||
event="chat_message_sent",
|
||||
properties=properties,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to track user message: {e}")
|
||||
|
||||
|
||||
def track_tool_called(
|
||||
user_id: str | None,
|
||||
session_id: str,
|
||||
tool_name: str,
|
||||
tool_call_id: str,
|
||||
) -> None:
|
||||
"""Track when a tool is called in chat.
|
||||
|
||||
Args:
|
||||
user_id: The user's ID (or None for anonymous)
|
||||
session_id: The chat session ID
|
||||
tool_name: Name of the tool being called
|
||||
tool_call_id: Unique ID of the tool call
|
||||
"""
|
||||
client = _get_posthog_client()
|
||||
if not client:
|
||||
return
|
||||
|
||||
try:
|
||||
properties = {
|
||||
**_get_base_properties(),
|
||||
"session_id": session_id,
|
||||
"tool_name": tool_name,
|
||||
"tool_call_id": tool_call_id,
|
||||
}
|
||||
client.capture(
|
||||
distinct_id=user_id or f"anonymous_{session_id}",
|
||||
event="chat_tool_called",
|
||||
properties=properties,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to track tool call: {e}")
|
||||
|
||||
|
||||
def track_agent_run_success(
|
||||
user_id: str,
|
||||
session_id: str,
|
||||
graph_id: str,
|
||||
graph_name: str,
|
||||
execution_id: str,
|
||||
library_agent_id: str,
|
||||
) -> None:
|
||||
"""Track when an agent is successfully run.
|
||||
|
||||
Args:
|
||||
user_id: The user's ID
|
||||
session_id: The chat session ID
|
||||
graph_id: ID of the agent graph
|
||||
graph_name: Name of the agent
|
||||
execution_id: ID of the execution
|
||||
library_agent_id: ID of the library agent
|
||||
"""
|
||||
client = _get_posthog_client()
|
||||
if not client:
|
||||
return
|
||||
|
||||
try:
|
||||
properties = {
|
||||
**_get_base_properties(),
|
||||
"session_id": session_id,
|
||||
"graph_id": graph_id,
|
||||
"graph_name": graph_name,
|
||||
"execution_id": execution_id,
|
||||
"library_agent_id": library_agent_id,
|
||||
}
|
||||
client.capture(
|
||||
distinct_id=user_id,
|
||||
event="chat_agent_run_success",
|
||||
properties=properties,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to track agent run: {e}")
|
||||
|
||||
|
||||
def track_agent_scheduled(
|
||||
user_id: str,
|
||||
session_id: str,
|
||||
graph_id: str,
|
||||
graph_name: str,
|
||||
schedule_id: str,
|
||||
schedule_name: str,
|
||||
cron: str,
|
||||
library_agent_id: str,
|
||||
) -> None:
|
||||
"""Track when an agent is successfully scheduled.
|
||||
|
||||
Args:
|
||||
user_id: The user's ID
|
||||
session_id: The chat session ID
|
||||
graph_id: ID of the agent graph
|
||||
graph_name: Name of the agent
|
||||
schedule_id: ID of the schedule
|
||||
schedule_name: Name of the schedule
|
||||
cron: Cron expression for the schedule
|
||||
library_agent_id: ID of the library agent
|
||||
"""
|
||||
client = _get_posthog_client()
|
||||
if not client:
|
||||
return
|
||||
|
||||
try:
|
||||
properties = {
|
||||
**_get_base_properties(),
|
||||
"session_id": session_id,
|
||||
"graph_id": graph_id,
|
||||
"graph_name": graph_name,
|
||||
"schedule_id": schedule_id,
|
||||
"schedule_name": schedule_name,
|
||||
"cron": cron,
|
||||
"library_agent_id": library_agent_id,
|
||||
}
|
||||
client.capture(
|
||||
distinct_id=user_id,
|
||||
event="chat_agent_scheduled",
|
||||
properties=properties,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to track agent schedule: {e}")
|
||||
|
||||
|
||||
def track_trigger_setup(
|
||||
user_id: str,
|
||||
session_id: str,
|
||||
graph_id: str,
|
||||
graph_name: str,
|
||||
trigger_type: str,
|
||||
library_agent_id: str,
|
||||
) -> None:
|
||||
"""Track when a trigger is set up for an agent.
|
||||
|
||||
Args:
|
||||
user_id: The user's ID
|
||||
session_id: The chat session ID
|
||||
graph_id: ID of the agent graph
|
||||
graph_name: Name of the agent
|
||||
trigger_type: Type of trigger (e.g., 'webhook')
|
||||
library_agent_id: ID of the library agent
|
||||
"""
|
||||
client = _get_posthog_client()
|
||||
if not client:
|
||||
return
|
||||
|
||||
try:
|
||||
properties = {
|
||||
**_get_base_properties(),
|
||||
"session_id": session_id,
|
||||
"graph_id": graph_id,
|
||||
"graph_name": graph_name,
|
||||
"trigger_type": trigger_type,
|
||||
"library_agent_id": library_agent_id,
|
||||
}
|
||||
client.capture(
|
||||
distinct_id=user_id,
|
||||
event="chat_trigger_setup",
|
||||
properties=properties,
|
||||
)
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to track trigger setup: {e}")
|
||||
@@ -666,6 +666,12 @@ class Secrets(UpdateTrackingModel["Secrets"], BaseSettings):
|
||||
default="https://cloud.langfuse.com", description="Langfuse host URL"
|
||||
)
|
||||
|
||||
# PostHog analytics
|
||||
posthog_api_key: str = Field(default="", description="PostHog API key")
|
||||
posthog_host: str = Field(
|
||||
default="https://us.i.posthog.com", description="PostHog host URL"
|
||||
)
|
||||
|
||||
# Add more secret fields as needed
|
||||
model_config = SettingsConfigDict(
|
||||
env_file=".env",
|
||||
|
||||
12
autogpt_platform/backend/poetry.lock
generated
12
autogpt_platform/backend/poetry.lock
generated
@@ -4204,14 +4204,14 @@ strenum = {version = ">=0.4.9,<0.5.0", markers = "python_version < \"3.11\""}
|
||||
|
||||
[[package]]
|
||||
name = "posthog"
|
||||
version = "6.1.1"
|
||||
version = "7.6.0"
|
||||
description = "Integrate PostHog into any python application."
|
||||
optional = false
|
||||
python-versions = ">=3.9"
|
||||
python-versions = ">=3.10"
|
||||
groups = ["main"]
|
||||
files = [
|
||||
{file = "posthog-6.1.1-py3-none-any.whl", hash = "sha256:329fd3d06b4d54cec925f47235bd8e327c91403c2f9ec38f1deb849535934dba"},
|
||||
{file = "posthog-6.1.1.tar.gz", hash = "sha256:b453f54c4a2589da859fd575dd3bf86fcb40580727ec399535f268b1b9f318b8"},
|
||||
{file = "posthog-7.6.0-py3-none-any.whl", hash = "sha256:c4dd78cf77c4fecceb965f86066e5ac37886ef867d68ffe75a1db5d681d7d9ad"},
|
||||
{file = "posthog-7.6.0.tar.gz", hash = "sha256:941dfd278ee427c9b14640f09b35b5bb52a71bdf028d7dbb7307e1838fd3002e"},
|
||||
]
|
||||
|
||||
[package.dependencies]
|
||||
@@ -4225,7 +4225,7 @@ typing-extensions = ">=4.2.0"
|
||||
[package.extras]
|
||||
dev = ["django-stubs", "lxml", "mypy", "mypy-baseline", "packaging", "pre-commit", "pydantic", "ruff", "setuptools", "tomli", "tomli_w", "twine", "types-mock", "types-python-dateutil", "types-requests", "types-setuptools", "types-six", "wheel"]
|
||||
langchain = ["langchain (>=0.2.0)"]
|
||||
test = ["anthropic", "coverage", "django", "freezegun (==1.5.1)", "google-genai", "langchain-anthropic (>=0.3.15)", "langchain-community (>=0.3.25)", "langchain-core (>=0.3.65)", "langchain-openai (>=0.3.22)", "langgraph (>=0.4.8)", "mock (>=2.0.0)", "openai", "parameterized (>=0.8.1)", "pydantic", "pytest", "pytest-asyncio", "pytest-timeout"]
|
||||
test = ["anthropic (>=0.72)", "coverage", "django", "freezegun (==1.5.1)", "google-genai", "langchain-anthropic (>=1.0)", "langchain-community (>=0.4)", "langchain-core (>=1.0)", "langchain-openai (>=1.0)", "langgraph (>=1.0)", "mock (>=2.0.0)", "openai (>=2.0)", "parameterized (>=0.8.1)", "pydantic", "pytest", "pytest-asyncio", "pytest-timeout"]
|
||||
|
||||
[[package]]
|
||||
name = "postmarker"
|
||||
@@ -7512,4 +7512,4 @@ cffi = ["cffi (>=1.11)"]
|
||||
[metadata]
|
||||
lock-version = "2.1"
|
||||
python-versions = ">=3.10,<3.14"
|
||||
content-hash = "18b92e09596298c82432e4d0a85cb6d80a40b4229bee0a0c15f0529fd6cb21a4"
|
||||
content-hash = "ee5742dc1a9df50dfc06d4b26a1682cbb2b25cab6b79ce5625ec272f93e4f4bf"
|
||||
|
||||
@@ -85,6 +85,7 @@ exa-py = "^1.14.20"
|
||||
croniter = "^6.0.0"
|
||||
stagehand = "^0.5.1"
|
||||
gravitas-md2gdocs = "^0.1.0"
|
||||
posthog = "^7.6.0"
|
||||
|
||||
[tool.poetry.group.dev.dependencies]
|
||||
aiohappyeyeballs = "^2.6.1"
|
||||
|
||||
Reference in New Issue
Block a user