From 3e020d703bf35e5ece42635faf55bd5f3fc4782f Mon Sep 17 00:00:00 2001 From: Fabian Schindler Date: Wed, 24 Jun 2026 18:46:21 +0200 Subject: [PATCH] docs(laravel): Add AI agent monitoring onboarding Add Laravel-specific agent monitoring setup instructions for laravel/ai and expose php-laravel as a supported platform for the agent monitoring and conversations onboarding flows. Refs TET-2515 --- static/app/data/platformCategories.tsx | 1 + .../php-laravel/agentMonitoring.tsx | 79 ++++++++++++ .../gettingStartedDocs/php-laravel/index.tsx | 2 + .../explore/conversations/onboarding.tsx | 122 +++++++++++++++--- .../agents/components/sdkUpdateAlert.spec.tsx | 35 +++++ .../agents/components/sdkUpdateAlert.tsx | 4 + .../insights/pages/agents/onboarding.tsx | 22 +++- .../pages/agents/utils/agentIntegrations.tsx | 2 + 8 files changed, 245 insertions(+), 22 deletions(-) create mode 100644 static/app/gettingStartedDocs/php-laravel/agentMonitoring.tsx diff --git a/static/app/data/platformCategories.tsx b/static/app/data/platformCategories.tsx index 4c3796b2c3b74b..7830502b35a0ca 100644 --- a/static/app/data/platformCategories.tsx +++ b/static/app/data/platformCategories.tsx @@ -840,6 +840,7 @@ export const agentMonitoringPlatforms: ReadonlySet = new Set([ ...platformKeys.filter(id => id.startsWith('python')), 'deno', 'bun', + 'php-laravel', ]); export const javascriptMetaFrameworks: readonly PlatformKey[] = [ diff --git a/static/app/gettingStartedDocs/php-laravel/agentMonitoring.tsx b/static/app/gettingStartedDocs/php-laravel/agentMonitoring.tsx new file mode 100644 index 00000000000000..a9395b092296ad --- /dev/null +++ b/static/app/gettingStartedDocs/php-laravel/agentMonitoring.tsx @@ -0,0 +1,79 @@ +import type {OnboardingConfig} from 'sentry/components/onboarding/gettingStartedDoc/types'; +import {StepType} from 'sentry/components/onboarding/gettingStartedDoc/types'; +import {t, tct} from 'sentry/locale'; +import {SdkUpdateAlert} from 'sentry/views/insights/pages/agents/components/sdkUpdateAlert'; + +const MIN_REQUIRED_VERSION = '4.27.0'; + +export const agentMonitoring: OnboardingConfig = { + introduction: params => ( + + ), + install: () => [ + { + type: StepType.INSTALL, + content: [ + { + type: 'text', + text: tct( + 'Agent monitoring for Laravel uses the [code:laravel/ai] package and [code:sentry/sentry-laravel] version [minVersion] or newer.', + { + code: , + minVersion: {MIN_REQUIRED_VERSION}, + } + ), + }, + { + type: 'code', + language: 'bash', + code: `composer require sentry/sentry-laravel:^${MIN_REQUIRED_VERSION} laravel/ai +php artisan vendor:publish --provider="Laravel\\Ai\\AiServiceProvider" +php artisan migrate`, + }, + ], + }, + ], + configure: params => [ + { + type: StepType.CONFIGURE, + content: [ + { + type: 'text', + text: t( + 'Configure Sentry. Choose yes when prompted to enable Performance Monitoring.' + ), + }, + { + type: 'code', + language: 'shell', + code: `php artisan sentry:publish --dsn=${params.dsn.public}`, + }, + ], + }, + ], + verify: () => [ + { + type: StepType.VERIFY, + content: [ + { + type: 'text', + text: t('Verify agent monitoring by calling any Laravel AI agent:'), + }, + { + type: 'code', + language: 'php', + code: `prompt('What time is it?');`, + }, + ], + }, + ], + nextSteps: () => [], +}; diff --git a/static/app/gettingStartedDocs/php-laravel/index.tsx b/static/app/gettingStartedDocs/php-laravel/index.tsx index d2b842cd92bfd4..b60420d7112c31 100644 --- a/static/app/gettingStartedDocs/php-laravel/index.tsx +++ b/static/app/gettingStartedDocs/php-laravel/index.tsx @@ -4,6 +4,7 @@ import { replayOnboardingJsLoader, } from 'sentry/gettingStartedDocs/javascript/jsLoader'; +import {agentMonitoring} from './agentMonitoring'; import {crashReport} from './crashReport'; import {logs} from './logs'; import {metrics} from './metrics'; @@ -18,4 +19,5 @@ export const docs: Docs = { profilingOnboarding: profiling, logsOnboarding: logs, metricsOnboarding: metrics, + agentMonitoringOnboarding: agentMonitoring, }; diff --git a/static/app/views/explore/conversations/onboarding.tsx b/static/app/views/explore/conversations/onboarding.tsx index fd8ab8c8259b4f..3f4cbedc7fa8b0 100644 --- a/static/app/views/explore/conversations/onboarding.tsx +++ b/static/app/views/explore/conversations/onboarding.tsx @@ -30,7 +30,10 @@ import type { DocsParams, OnboardingStep, } from 'sentry/components/onboarding/gettingStartedDoc/types'; -import {DocsPageLocation} from 'sentry/components/onboarding/gettingStartedDoc/types'; +import { + DocsPageLocation, + StepType, +} from 'sentry/components/onboarding/gettingStartedDoc/types'; import {useSourcePackageRegistries} from 'sentry/components/onboarding/gettingStartedDoc/useSourcePackageRegistries'; import {useLoadGettingStarted} from 'sentry/components/onboarding/gettingStartedDoc/utils/useLoadGettingStarted'; import {PlatformOptionDropdown} from 'sentry/components/onboarding/platformOptionDropdown'; @@ -57,6 +60,7 @@ import { AGENT_INTEGRATION_LABELS, AgentIntegration, NODE_AGENT_INTEGRATIONS, + PHP_AGENT_INTEGRATIONS, PYTHON_AGENT_INTEGRATIONS, } from 'sentry/views/insights/pages/agents/utils/agentIntegrations'; import {AI_INSTRUMENTATION_DOCS_LINKS} from 'sentry/views/insights/pages/agents/utils/docsLinks'; @@ -225,21 +229,51 @@ function ConversationOnboardingPanel({ ); } -function getConversationIdStep(integration: string, isPython: boolean): OnboardingStep { +function getConversationIdStep( + integration: string, + platform: 'javascript' | 'php' | 'python' +): OnboardingStep { const isOpenAI = integration === AgentIntegration.OPENAI || integration === AgentIntegration.OPENAI_AGENTS; - const content: ContentBlock[] = [ - { - type: 'text', - text: t( - 'Group related LLM calls into a single conversation thread by setting an ID at the start:' - ), - }, - isPython + if (platform === 'php') { + return { + title: t('Enable Conversations'), + content: [ + { + type: 'text', + text: tct( + 'Make your Laravel AI agent conversational with the [code:Conversational] contract and [code:RemembersConversations] trait:', + {code: } + ), + }, + { + type: 'code', + language: 'php', + code: `continue()]:', + {code: } + ), + }, + { + type: 'code', + language: 'php', + code: `continue('my-conversation-123', as: auth()->user()) + ->prompt('Make it shorter.');`, + }, + ], + }; +} + export function ConversationOnboarding({onDismiss}: {onDismiss: () => void}) { const api = useApi(); const {isSelfHosted, urlPrefix} = useLegacyStore(ConfigStore); @@ -293,19 +362,31 @@ export function ConversationOnboarding({onDismiss}: {onDismiss: () => void}) { }); const isPythonPlatform = (project?.platform ?? '').startsWith('python'); + const isPhpPlatform = (project?.platform ?? '').startsWith('php'); const integrations = isPythonPlatform ? PYTHON_AGENT_INTEGRATIONS - : NODE_AGENT_INTEGRATIONS; + : isPhpPlatform + ? PHP_AGENT_INTEGRATIONS + : NODE_AGENT_INTEGRATIONS; const integrationOptions = { integration: { label: t('Integration'), items: integrations.map(integration => ({ - label: AGENT_INTEGRATION_LABELS[integration], + label: isPhpPlatform + ? (currentPlatform?.name ?? t('Laravel')) + : AGENT_INTEGRATION_LABELS[integration], value: integration, leadingItems: ( - + ), })), }, @@ -368,8 +449,13 @@ export function ConversationOnboarding({onDismiss}: {onDismiss: () => void}) { const steps: OnboardingStep[] = [ ...(agentMonitoringDocs.install?.(docParams) || []), ...(agentMonitoringDocs.configure?.(docParams) || []), - getConversationIdStep(selectedIntegration, isPythonPlatform), - ...(agentMonitoringDocs.verify?.(docParams) || []), + getConversationIdStep( + selectedIntegration, + isPythonPlatform ? 'python' : isPhpPlatform ? 'php' : 'javascript' + ), + ...(isPhpPlatform + ? [getPhpConversationVerifyStep()] + : agentMonitoringDocs.verify?.(docParams) || []), ].filter(s => !s.collapsible); const introduction = agentMonitoringDocs.introduction?.(docParams); diff --git a/static/app/views/insights/pages/agents/components/sdkUpdateAlert.spec.tsx b/static/app/views/insights/pages/agents/components/sdkUpdateAlert.spec.tsx index d454316b94ddbe..06e48526cafc01 100644 --- a/static/app/views/insights/pages/agents/components/sdkUpdateAlert.spec.tsx +++ b/static/app/views/insights/pages/agents/components/sdkUpdateAlert.spec.tsx @@ -194,6 +194,41 @@ describe('SdkUpdateAlert', () => { ).toBeInTheDocument(); }); + it('renders alert when Laravel SDK version is below minimum', async () => { + renderMockSdkUpdateRequest({ + organization, + body: [ + { + projectId: project.id, + sdkName: 'sentry.php.laravel', + sdkVersion: '4.26.0', + suggestions: [{type: 'updateSdk', newSdkVersion: '4.27.0'}], + }, + ], + }); + + render( + , + {organization} + ); + + expect( + await screen.findByText( + textWithMarkupMatcher( + 'Your sentry/sentry-laravel version is below the minimum required for agent monitoring.' + ) + ) + ).toBeInTheDocument(); + + expect( + screen.getByText(textWithMarkupMatcher('Update to 4.27.0 or later.')) + ).toBeInTheDocument(); + }); + it('does not render when only a non-matching SDK has updates', async () => { // Reproduces a real scenario: a Flask project also receives Rust events. // The Rust SDK has an update suggestion but the Flask SDK is up-to-date. diff --git a/static/app/views/insights/pages/agents/components/sdkUpdateAlert.tsx b/static/app/views/insights/pages/agents/components/sdkUpdateAlert.tsx index 28d6f154ff7932..df39a80fffa270 100644 --- a/static/app/views/insights/pages/agents/components/sdkUpdateAlert.tsx +++ b/static/app/views/insights/pages/agents/components/sdkUpdateAlert.tsx @@ -24,6 +24,10 @@ function getPackageNameFromSdkName(sdkName?: string): string | null { } } + if (sdkName === 'sentry.php.laravel') { + return 'sentry/sentry-laravel'; + } + return null; } diff --git a/static/app/views/insights/pages/agents/onboarding.tsx b/static/app/views/insights/pages/agents/onboarding.tsx index e91f653bd0e92d..1744698ae23b48 100644 --- a/static/app/views/insights/pages/agents/onboarding.tsx +++ b/static/app/views/insights/pages/agents/onboarding.tsx @@ -58,6 +58,7 @@ import { AGENT_INTEGRATION_LABELS, DENO_AGENT_INTEGRATIONS, NODE_AGENT_INTEGRATIONS, + PHP_AGENT_INTEGRATIONS, PYTHON_AGENT_INTEGRATIONS, } from 'sentry/views/insights/pages/agents/utils/agentIntegrations'; import {getHasAiSpansFilter} from 'sentry/views/insights/pages/agents/utils/query'; @@ -245,21 +246,33 @@ export function Onboarding() { // Local integration options for Agent Monitoring only const isPythonPlatform = (project?.platform ?? '').startsWith('python'); const isDenoPlatform = project?.platform === 'deno'; + const isPhpPlatform = (project?.platform ?? '').startsWith('php'); const integrations = isPythonPlatform ? PYTHON_AGENT_INTEGRATIONS : isDenoPlatform ? DENO_AGENT_INTEGRATIONS - : NODE_AGENT_INTEGRATIONS; + : isPhpPlatform + ? PHP_AGENT_INTEGRATIONS + : NODE_AGENT_INTEGRATIONS; const integrationOptions = { integration: { label: t('Integration'), items: integrations.map(integration => ({ - label: AGENT_INTEGRATION_LABELS[integration], + label: isPhpPlatform + ? (currentPlatform?.name ?? t('Laravel')) + : AGENT_INTEGRATION_LABELS[integration], value: integration, leadingItems: ( - + ), })), }, @@ -335,8 +348,9 @@ export function Onboarding() {

{tct( - 'To use [link:Conversations], set a conversation ID for each chat. Sentry uses the `gen_ai.conversation.id` attribute to group related AI spans.', + 'To use [link:Conversations], set a conversation ID for each chat. Sentry uses the [code:gen_ai.conversation.id] attribute to group related AI spans.', { + code: , link: ( ), diff --git a/static/app/views/insights/pages/agents/utils/agentIntegrations.tsx b/static/app/views/insights/pages/agents/utils/agentIntegrations.tsx index c9dbb850046690..e7951d96f66db3 100644 --- a/static/app/views/insights/pages/agents/utils/agentIntegrations.tsx +++ b/static/app/views/insights/pages/agents/utils/agentIntegrations.tsx @@ -67,3 +67,5 @@ export const DENO_AGENT_INTEGRATIONS = [ AgentIntegration.VERCEL_AI, AgentIntegration.MANUAL, ]; + +export const PHP_AGENT_INTEGRATIONS = [AgentIntegration.MANUAL];