npm package: @grafana/assistant
Description: Type definitions and helper functions for Grafana Assistant
Author: Grafana Labs
Latest version: 0.1.24

# @grafana/assistant

Type definitions and utilities for integrating with the Grafana Assistant.

## Installation

```bash
npm install @grafana/assistant
```

## Usage

### Checking Assistant Availability

```typescript
import { isAssistantAvailable } from '@grafana/assistant';

// Check if the assistant is available
isAssistantAvailable().subscribe((available) => {
  if (available) {
    console.log('Assistant is available');
  }
});
```

### Using the Assistant Hook (React)

```typescript
import { useAssistant } from '@grafana/assistant';

function MyComponent() {
  const { isLoading, isAvailable, openAssistant, closeAssistant } = useAssistant();

  if (isLoading) {
    return <div>Loading...</div>;
  }

  if (!isAvailable || !openAssistant) {
    return <div>Assistant not available</div>;
  }

  return (
    <button onClick={() => openAssistant({
      origin: 'grafana/panel-data-analyzer',
      prompt: 'Help me analyze data' })}>
      Open Assistant
    </button>
  );
}
```

### Opening the Assistant Sidebar

```typescript
import { openAssistant, closeAssistant } from '@grafana/assistant';

// Open the assistant with an initial prompt
openAssistant({ origin: 'grafana/some-feature', prompt: 'Show me CPU usage over the last hour' });

// Open the assistant with initial prompt and autoSend set to false
openAssistant({
  origin: 'grafana-datasources/prometheus/some-feature',
  prompt: 'Help me analyze data',
  autoSend: false,
});

// Open the assistant without an initial prompt
openAssistant({
  origin: 'grafana-slo-app/some-page',
});

// Close the assistant
closeAssistant();
```

### Using Context with the Assistant

```typescript
import { openAssistant, createAssistantContextItem } from '@grafana/assistant';

// Create context items to provide additional information to the assistant
const datasourceContext = createAssistantContextItem('datasource', {
  datasourceUid: 'prometheus-uid',
});

const dashboardContext = createAssistantContextItem('dashboard', {
  dashboardUid: 'dashboard-uid',
  dashboardTitle: 'System Overview',
  folderUid: 'folder-uid',
  folderTitle: 'Production',
});

// Open the assistant with initial prompt and context
openAssistant({
  origin: 'grafana/dashboard-page',
  prompt: 'Analyze the CPU metrics from this dashboard',
  context: [datasourceContext, dashboardContext],
});

// Create hidden context for system instructions (won't show in UI pills)
const systemInstructions = createAssistantContextItem('structured', {
  hidden: true,
  title: 'System Instructions',
  data: {
    instructions: 'When analyzing metrics, always consider seasonal patterns and anomalies',
    preferences: 'Use clear, technical language and provide actionable insights',
  },
});

// Include hidden context with visible context
openAssistant({
  prompt: 'What are the trends in this data?',
  context: [datasourceContext, systemInstructions],
});
```

### Providing Page-Specific Context

You can register context items that are automatically included when the assistant is opened on pages matching specific URL patterns. The `providePageContext` function returns a setter function that allows you to update the context dynamically:

```typescript
import { providePageContext, createAssistantContextItem } from '@grafana/assistant';

// Create initial context for dashboard pages
const dashboardContext = createAssistantContextItem('structured', {
  data: {
    name: 'Dashboard Context',
    pageType: 'dashboard',
    capabilities: ['view', 'edit', 'share'],
    help: 'Use the assistant to analyze dashboard data, create queries, or troubleshoot issues.',
  },
});

// Register context for all dashboard pages and get a setter function
const setContext = providePageContext('/d/*', [dashboardContext]);

// Later, dynamically update the context based on user interactions or data changes
setContext([
  dashboardContext,
  createAssistantContextItem('structured', {
    data: {
      name: 'Panel Context',
      selectedPanel: 'cpu-usage-panel',
      panelType: 'graph',
    },
  }),
]);

// Clean up when no longer needed
setContext.unregister();

// Example: Provide hidden system instructions for explore pages
providePageContext('/explore', [
  createAssistantContextItem('structured', {
    hidden: true,
    title: 'Additional instructions',
    data: {
      'System Instructions':
        'When working with explore queries, always validate the time range and check for data availability. Suggest using recording rules for expensive queries.',
    },
  }),
]);
```

### Using Page Context in React Components

For React components, use the `useProvidePageContext` hook which automatically handles cleanup:

```typescript
import { useProvidePageContext, createAssistantContextItem } from '@grafana/assistant';

function DashboardComponent() {
  const dashboardContext = createAssistantContextItem('structured', {
    data: {
      name: 'Dashboard Context',
      pageType: 'dashboard'
    }
  });

  // Automatically cleans up on unmount
  const setContext = useProvidePageContext('/d/*', [dashboardContext]);

  // Update context when needed
  const handlePanelSelect = (panelId: string) => {
    setContext([
      dashboardContext,
      createAssistantContextItem('structured', {
        data: {
          name: 'Panel Context',
          selectedPanel: panelId
        }
      })
    ]);
  };

  return <div>Dashboard content</div>;
}
```

### Accessing Current Page Context

Use the `usePageContext` hook to access all active context for the current page:

```typescript
import { usePageContext } from '@grafana/assistant';

function MyComponent() {
  const pageContext = usePageContext();

  console.log('Active context items:', pageContext);

  return <div>Page has {pageContext.length} context items</div>;
}
```

### Creating Context Items

The `createAssistantContextItem` function allows you to create structured context items that provide the assistant with additional information about your Grafana resources.

```typescript
import { createAssistantContextItem } from '@grafana/assistant';

// Create a datasource context
const datasourceContext = createAssistantContextItem('datasource', {
  datasourceUid: 'prom-123',
  title: 'Main Datasource', // Optional custom title
  icon: 'database', // Optional custom icon
});

// Create a dashboard context
const dashboardContext = createAssistantContextItem('dashboard', {
  dashboardUid: 'dash-456',
  dashboardTitle: 'Application Overview',
  folderUid: 'folder-789',
  folderTitle: 'Production Dashboards',
});

// Create a label context
const labelContext = createAssistantContextItem('label_name', {
  datasourceUid: 'prom-123',
  datasourceType: 'prometheus',
  labelName: 'service',
});

// Create a label value context
const labelValueContext = createAssistantContextItem('label_value', {
  datasourceUid: 'prom-123',
  datasourceType: 'prometheus',
  labelName: 'service',
  labelValue: 'api-server',
});

// Create a folder context
const folderContext = createAssistantContextItem('dashboard_folder', {
  folderUid: 'folder-789',
  folderTitle: 'Production Dashboards',
});

// Create structured data context
const structuredContext = createAssistantContextItem('structured', {
  data: {
    name: 'Custom Data',
    metrics: ['cpu_usage', 'memory_usage'],
    threshold: 80,
  },
});

// Create generic context
const genericContext = createAssistantContextItem('unknown', {
  id: 'my-context',
  text: 'Some additional context information',
});

// Create component context for custom React components
const componentContext = createAssistantContextItem('component', {
  components: {
    MyCustomComponent: MyCustomComponentImplementation,
    AnotherComponent: AnotherComponentImplementation,
  },
  prompt: `
- When mentioning specific entities, use the your_app_MyCustomComponent with the syntax: <your_app_MyCustomComponent prop1={'value1'} prop2={'value2'} />
- Custom components must never be wrapped in code blocks (backticks \` or \`\`\`).
- Always provide all required props when using custom components.
`,
  namespace: 'your_app', // Optional: Namespace for your components (defaults to 'components')
});

// Create hidden context (useful for system instructions)
const hiddenInstructions = createAssistantContextItem('structured', {
  hidden: true, // Won't be shown in UI pills but will be sent to the assistant
  title: 'Page-specific instructions',
  data: {
    instructions: 'Always provide step-by-step explanations',
    context: 'This is a complex system with multiple dependencies',
  },
});
```

### Registering Custom Components for Assistant Usage

The `component` context type allows you to register custom React components that the assistant can use in its responses. This is particularly useful for rendering domain-specific UI elements that enhance the user experience.

> **⚠️ Important:** Components must be approved by #grafana-assistant before usage. Please reach out to the Grafana Assistant team for review and approval of your custom components before implementing them in production.

#### Method 1: Using provideComponents (Recommended)

```typescript
import { provideComponents } from '@grafana/assistant';
import MyEntityMention from 'components/MyEntityMentionComponent';
import MyCustomChart from 'components/MyCustomChartComponent';

/**
 * Configure the assistant context for your app plugin.
 * This sets up custom components that will be available to the Grafana Assistant
 * when users are on any page matching the provided pattern.
 */
export function configureAssistantContext() {
  provideComponents(
    `
    - When mentioning an entity in text, always use the your_app_MyEntityMention component: <your_app_MyEntityMention name={'entity name here'} type={'entity type here'} />
    - For displaying charts, use the your_app_MyCustomChart component: <your_app_MyCustomChart data={chartData} title={'Chart Title'} />
    - Custom components must never be wrapped in code blocks (backticks \` or \`\`\`).
    `,
    'your_app', // namespace
    {
      MyEntityMention,
      MyCustomChart,
    },
    /.*/ // matches all pages
  );
}
```

#### Method 2: Using createAssistantContextItem with providePageContext

```typescript
import { createAssistantContextItem, providePageContext } from '@grafana/assistant';
import MyEntityMention from 'components/MyEntityMentionComponent';
import MyCustomChart from 'components/MyCustomChartComponent';

export function configureAssistantContext() {
  providePageContext(/.*/, [
    createAssistantContextItem('component', {
      components: {
        MyEntityMention,
        MyCustomChart,
      },
      prompt: `
        - When mentioning an entity in text, always use the your_app_MyEntityMention component: <your_app_MyEntityMention name={'entity name here'} type={'entity type here'} />
        - For displaying charts, use the your_app_MyCustomChart component: <your_app_MyCustomChart data={chartData} title={'Chart Title'} />
        - Custom components must never be wrapped in code blocks (backticks \` or \`\`\`).
      `,
      namespace: 'your_app', // Optional: defaults to 'components' if not provided
    }),
  ]);
}
```

**Key Points for Component Context:**

- **Approval Required**: ⚠️ Components must be approved by #grafana-assistant before usage
- **Components**: A record of component names to their React component implementations
- **Prompt**: Instructions for the assistant on how and when to use these components
- **Namespace**: A unique identifier for your app's components (e.g., 'your_app', 'datasource'). Optional - defaults to 'components' if not provided
- **Component Usage**: Components are referenced in assistant responses as `<namespace_ComponentName prop={'value'} />` (note the underscore format)
- **No Code Blocks**: Custom components should never be wrapped in markdown code blocks

**Example Component Implementation:**

```typescript
interface MyEntityMentionProps {
  name: string;
  type: string;
  env?: string;
  site?: string;
  namespace?: string;
  properties?: string;
}

const MyEntityMention: React.FC<MyEntityMentionProps> = ({ name, type, env, site, namespace, properties }) => {
  return (
    <span
      className="entity-mention"
      data-entity-type={type}
      title={`${type}: ${name}`}
    >
      {name}
    </span>
  );
};

export default MyEntityMention;
```

### Using Hidden Context for System Instructions

The `hidden` parameter allows you to provide context to the assistant without showing it in the UI pills. This is particularly useful for:

- **System instructions**: Provide behavioral guidelines specific to certain pages
- **Metadata**: Include technical details that might confuse users if shown
- **Contextual rules**: Add page-specific rules without cluttering the interface

Example use cases:

```typescript
// Provide exploration-specific guidelines
providePageContext('/explore', [
  createAssistantContextItem('structured', {
    hidden: true,
    title: 'Explore Page Guidelines',
    data: {
      instructions: [
        'Always validate time ranges before running queries',
        'Suggest using recording rules for expensive queries',
        'Warn about queries that might impact performance',
      ],
      queryLimits: {
        maxTimeRange: '24h',
        suggestedSampleInterval: '1m',
      },
    },
  }),
]);

// Add dashboard-specific context
providePageContext('/d/*', [
  createAssistantContextItem('structured', {
    hidden: true,
    title: 'Dashboard Instructions',
    data: {
      bestPractices: 'Encourage users to use template variables for flexibility',
      performance: 'Suggest panel caching for slow queries',
    },
  }),
]);
```

### Exposing Functions to the Assistant

```typescript
import {
  getExposeAssistantFunctionsConfig,
  newFunctionNamespace,
  FunctionImplementation,
  FunctionNamespace,
  NamedFunctions,
} from '@grafana/assistant';

// Define your functions
const myFunctions: NamedFunctions = {
  getMetrics: (datasource: string) => {
    return ['cpu_usage', 'memory_usage', 'disk_io'];
  },

  calculateAverage: (values: number[]) => {
    return values.reduce((a, b) => a + b, 0) / values.length;
  },
};

// Create a namespace for your functions
const namespace = newFunctionNamespace('myPlugin', myFunctions);

// Export the configuration for your plugin
export const getExtensionConfigs = () => [getExposeAssistantFunctionsConfig([namespace])];
```

### Using the OpenAssistantButton Component

The `OpenAssistantButton` is a pre-built Button component that provides an easy way to add assistant functionality to your UI with a single button click. It automatically checks availability and hides itself if the Assistant is not available.

```typescript
import { OpenAssistantButton, createAssistantContextItem } from '@grafana/assistant';

function MyDashboard() {
  const dashboardContext = createAssistantContextItem('dashboard', {
    dashboardUid: 'dashboard-123',
    dashboardTitle: 'CPU Metrics Dashboard'
  });

  return (
    <div>
      <h1>Dashboard</h1>

      {/* Basic usage */}
      <OpenAssistantButton
        prompt="Help me analyze the CPU usage trends in this dashboard"
        context={[dashboardContext]}
        origin="grafana/dashboard-menu"
      />

      {/* Icon-only button */}
      <OpenAssistantButton
        prompt="Show me optimization suggestions"
        iconOnlyButton={true}
        name="Get AI suggestions"
        size="md"
        origin="grafana-datasources/loki/query-editor"
      />

      {/* Button without auto-send */}
      <OpenAssistantButton
        prompt="What patterns do you see in the data?"
        autoSend={false}
        name="Ask Assistant"
        size="lg"
        origin="grafana/panel-viewer"
      />
    </div>
  );
}
```

## API Reference

### React Hooks

#### `useAssistant(): AssistantHook`

A React hook that provides assistant availability status and control functions.

**Returns:** An object with the following properties:

- `isLoading: boolean` - Whether the availability check is still in progress
- `isAvailable: boolean` - Whether the Assistant is available
- `openAssistant?: (props: OpenAssistantProps) => void` - Function to open the assistant (undefined if not available)
- `closeAssistant?: () => void` - Function to close the assistant (undefined if not available)
- `toggleAssistant?: (props: OpenAssistantProps) => void` - Function to toggle the assistant (undefined if not available)

#### `useTerms(): { accepted: boolean; loading: boolean; error: string | null }`

A React hook to check terms and conditions acceptance status. Automatically fetches terms data on mount and provides loading/error states.

**Returns:** An object with the following properties:

- `accepted: boolean` - Whether terms are accepted and up-to-date
- `loading: boolean` - Whether the check is in progress
- `error: string | null` - Error message if the check failed, null otherwise

#### `useLimits(): LimitsHookResult`

A React hook to check user prompt limits. Automatically fetches limits data on mount and listens to real-time updates via custom events when the Assistant plugin updates limits.

**Returns:** An object with the following properties:

- `count: number` - Current prompt count for the month
- `limit: number` - Monthly prompt limit (0 = unlimited)
- `month: string` - Current month in YYYY-MM format
- `isLimitReached: boolean` - Whether the limit has been reached
- `loading: boolean` - Whether the initial fetch is in progress
- `error: string | null` - Error message if the fetch failed, null otherwise
- `refetch: () => void` - Function to manually refetch limits

**Example:**

```typescript
import { useLimits } from '@grafana/assistant';

function MyComponent() {
  const { count, limit, isLimitReached, loading, error } = useLimits();

  if (loading) return <div>Loading...</div>;
  if (error) return <div>Error: {error}</div>;
  if (isLimitReached) {
    return <div>Monthly limit reached ({count}/{limit})</div>;
  }
  return <div>Prompts used: {count}/{limit || 'unlimited'}</div>;
}
```

#### `checkLimits(): Promise<LimitsData>`

An async function to check user prompt limits. Returns a promise with the limits data.

**Returns:** A promise that resolves to an object with:

- `count: number` - Current prompt count for the month
- `limit: number` - Monthly prompt limit (0 = unlimited)
- `month: string` - Current month in YYYY-MM format
- `isLimitReached: boolean` - Whether the limit has been reached

**Example:**

```typescript
import { checkLimits } from '@grafana/assistant';

async function checkUserLimits() {
  try {
    const { isLimitReached, count, limit } = await checkLimits();
    if (isLimitReached) {
      console.log(`Limit reached: ${count}/${limit}`);
    }
  } catch (error) {
    console.error('Failed to check limits:', error);
  }
}
```

#### `usePageContext(): ChatContextItem[]`

A React hook that returns all active context items for the current page based on registered URL patterns.

**Returns:** Array of `ChatContextItem` objects that match the current page URL.

#### `useProvidePageContext(urlPattern: string | RegExp, initialContext?: ChatContextItem[]): (context: ChatContextItem[]) => void`

A React hook for providing page context that automatically cleans up on unmount. This is the recommended way to use page context in React components.

**Parameters:**

- `urlPattern` - URL pattern (string or RegExp) to match against page URLs
- `initialContext` - Initial array of ChatContextItem to provide when the pattern matches (defaults to empty array)

**Returns:** A setter function to update the context

#### `usePageComponents(): Record<string, ComponentImplementation>`

A React hook that provides all components available for the current page. This hook automatically updates when the URL changes or when component registrations change.

**Returns:** Object containing all components available for the current page, keyed by `namespace_componentName` format.

**Example:**

```typescript
import { usePageComponents } from '@grafana/assistant';

function MyAssistantComponent() {
  const components = usePageComponents();

  // Access components registered with namespace 'myapp' and component name 'EntityMention'
  const EntityMentionComponent = components['myapp_EntityMention'];

  return (
    <div>
      Available components: {Object.keys(components).join(', ')}
    </div>
  );
}
```

### React Components

#### `<OpenAssistantButton />`

A pre-built React component that renders a button to open the Grafana Assistant with configurable prompt and context.

**Parameters:**

- `prompt: string` - **Required** The initial prompt to display in the assistant
- `origin: string` - **Required** Origin of the request that opened the assistant. Should be structured using forward slashes with the first part as a namespace. Examples: 'grafana-datasources/prometheus/query-builder', 'grafana-slo-app/slo-editor-overview'
- `context?: ChatContextItem[]` - Optional context items created with `createAssistantContextItem`
- `autoSend?: boolean` - Whether to automatically send the initial prompt (defaults to `true`)
- `title?: string` - Text to display on the button (defaults to `'Analyze with Assistant'`)
- `size?: 'xs' | 'sm' | 'md' | 'lg'` - Button size (defaults to `'sm'`)
- `iconOnlyButton?: boolean` - If `true`, renders as an icon-only button with tooltip (defaults to `false`)

#### `<QueryWithAssistantButton />`

A button component designed for query editors that opens the Assistant sidebar pre-populated with query context, datasource information, and an actionable prompt. It automatically detects whether the user is creating a new query or updating an existing one and adjusts the prompt accordingly. Renders nothing if the Assistant is not available.

**Parameters:**

- `size?: ComponentSize` - Button size (`'xs'`, `'sm'`, `'md'`, `'lg'`), defaults to `'sm'`
- `currentQuery: DataQuery` - **Required** The current query being edited
- `queries: DataQuery[]` - **Required** All queries in the editor (used to provide context about sibling queries)
- `dataSourceInstanceSettings: DataSourceInstanceSettings` - **Required** The datasource instance settings
- `app?: CoreApp` - The app context (e.g., `CoreApp.Explore`, `CoreApp.Dashboard`)
- `datasourceApi: DataSourceApi | null` - **Required** The datasource API instance (used to get query display text)
- `title?: string` - Button text (defaults to `'Query with Assistant'`)

**Example:**

```typescript
import { QueryWithAssistantButton } from '@grafana/assistant';
import { CoreApp } from '@grafana/data';

function QueryEditor({ query, queries, datasource, datasourceSettings }) {
  return (
    <div>
      {/* Your query editor UI */}
      <QueryWithAssistantButton
        size="sm"
        currentQuery={query}
        queries={queries}
        dataSourceInstanceSettings={datasourceSettings}
        app={CoreApp.Explore}
        datasourceApi={datasource}
      />

      {/* With custom title */}
      <QueryWithAssistantButton
        size="sm"
        currentQuery={query}
        queries={queries}
        dataSourceInstanceSettings={datasourceSettings}
        app={CoreApp.Explore}
        datasourceApi={datasource}
        title="Ask AI"
      />
    </div>
  );
}
```

#### `<AITextInput />`

An AI-powered text input component that uses the inline assistant to generate content. Features a gradient border animation while generating and an integrated send button. Perfect for generating short-form content like titles, labels, or names.

**Parameters:**

- `value: string` - **Required** The current value of the input (controlled component)
- `onChange: (value: string) => void` - **Required** Called when the input value changes
- `origin: string` - **Required** Origin for analytics tracking (e.g., 'grafana/panel-editor/title')
- `placeholder?: string` - Placeholder text (defaults to 'Ask AI to generate...')
- `systemPrompt?: string` - Optional system prompt to guide the assistant's behavior
- `onComplete?: (text: string) => void` - Callback when generation completes successfully
- `onError?: (error: Error) => void` - Callback if an error occurs during generation
- `onDelta?: (delta: string) => void` - Callback for streaming tokens as they are received during generation
- `disabled?: boolean` - Whether the input is disabled
- `autoGenerate?: boolean` - When true, automatically generates content on mount if value is empty (uses `systemPrompt` as the generation prompt)
- `streaming?: boolean` - When true, streams generated text into the input in real-time. When false (default), shows a placeholder until generation completes
- `className?: string` - Additional CSS class name
- `data-testid?: string` - Test ID for testing

**Example:**

```typescript
import { AITextInput } from '@grafana/assistant';
import { useState } from 'react';

function PanelEditor() {
  const [title, setTitle] = useState('');

  return (
    <AITextInput
      value={title}
      onChange={setTitle}
      origin="grafana/panel-editor/title"
      systemPrompt="Generate a concise, descriptive panel title. Return only the title text."
      placeholder="Ask AI to generate a title..."
      autoGenerate
      streaming
      onComplete={(text) => {
        setTitle(text);
        console.log('Generated:', text);
      }}
    />
  );
}
```

#### `<AITextArea />`

An AI-powered textarea component that uses the inline assistant to generate longer content. Features a gradient border animation while generating and an integrated send button. Use Cmd/Ctrl+Enter to submit. Ideal for descriptions, documentation, or multi-line content generation.

**Parameters:**

- `value: string` - **Required** The current value of the textarea (controlled component)
- `onChange: (value: string) => void` - **Required** Called when the textarea value changes
- `origin: string` - **Required** Origin for analytics tracking (e.g., 'grafana/panel-editor/description')
- `placeholder?: string` - Placeholder text (defaults to 'Ask AI to generate... (Cmd/Ctrl+Enter to send)')
- `systemPrompt?: string` - Optional system prompt to guide the assistant's behavior
- `onComplete?: (text: string) => void` - Callback when generation completes successfully
- `onError?: (error: Error) => void` - Callback if an error occurs during generation
- `onDelta?: (delta: string) => void` - Callback for streaming tokens as they are received during generation
- `disabled?: boolean` - Whether the textarea is disabled
- `autoGenerate?: boolean` - When true, automatically generates content on mount if value is empty (uses `systemPrompt` as the generation prompt)
- `streaming?: boolean` - When true, streams generated text into the textarea in real-time. When false (default), shows a placeholder until generation completes
- `rows?: number` - Number of rows for the textarea (defaults to 4)
- `className?: string` - Additional CSS class name
- `data-testid?: string` - Test ID for testing

**Example:**

```typescript
import { AITextArea } from '@grafana/assistant';
import { useState } from 'react';

function DashboardEditor() {
  const [description, setDescription] = useState('');

  return (
    <AITextArea
      value={description}
      onChange={setDescription}
      origin="grafana/dashboard-editor/description"
      systemPrompt="Generate a clear dashboard description in 2-3 sentences."
      placeholder="Ask AI to generate a description... (Cmd/Ctrl+Enter to send)"
      rows={6}
      autoGenerate
      streaming
      onComplete={(text) => {
        setDescription(text);
        console.log('Generated:', text);
      }}
    />
  );
}
```

### Using the Inline Assistant

The `useInlineAssistant` hook generates content inline without opening the sidebar — perfect for filling form fields with AI-generated text.

```typescript
import { useInlineAssistant } from '@grafana/assistant';
import { useState } from 'react';

export function PanelEditor() {
  const [title, setTitle] = useState('');
  const titleGen = useInlineAssistant();

  return (
    <div>
      <Input
        value={titleGen.isGenerating ? titleGen.content : title}
        onChange={(e) => setTitle(e.target.value)}
      />
      <Button
        onClick={() => {
          titleGen.generate({
            prompt: 'Generate a panel title for CPU metrics',
            origin: 'my-plugin/panel-editor',
            agentName: 'panel-title-generator',
            agentId: 'v1',
            onComplete: (text) => setTitle(text),
            onError: (err) => console.error(err)
          });
        }}
        disabled={titleGen.isGenerating}
      >
        {titleGen.isGenerating ? 'Generating...' : 'Generate'}
      </Button>
    </div>
  );
}
```

**Key Features:**

- `isGenerating` - Boolean indicating generation is in progress
- `content` - Current streaming content (updates in real-time)
- `generate(options)` - Start a new generation
- Each instance is independent—use multiple for concurrent fields

For instrumentation, keep `origin` focused on where the request came from and use `agentName` for the stable logical inline agent within that integration. The SDK namespaces `agentName` by the `origin` namespace, so different integrations can safely reuse names like `title-generator` without colliding in Sigil. Use `agentId` when you also want to tag a version or implementation identifier.

#### Using Tools with Inline Assistant

Tools extend the assistant's capabilities with custom actions. Tools can return both text and structured data (artifacts). To access artifact data, include an `onToolComplete` callback in your tool implementation.

```typescript
import { createTool } from '@grafana/assistant';
import { z } from 'zod';
import type { ToolInvokeOptions, ToolOutput } from '@grafana/assistant';

// Define artifact type
interface WeatherData {
  city: string;
  temperature: { celsius: number; fahrenheit: number };
  conditions: string;
  humidity: number;
}

// Create tool schema
const weatherSchema = z.object({
  city: z.string().describe('The city name to get weather for'),
});

// Create tool that accepts onToolComplete callback
export const createWeatherTool = (onToolComplete: (data: WeatherData) => void) => {
  return createTool(
    async (input: z.infer<typeof weatherSchema>): Promise<ToolOutput> => {
      const { city } = input;

      // Fetch weather data (simplified example)
      const weatherData: WeatherData = {
        city,
        temperature: { celsius: 22, fahrenheit: 72 },
        conditions: 'Sunny',
        humidity: 65,
      };

      // Call the callback to pass artifact data
      onToolComplete(weatherData);

      // Return tuple: [text response, artifact]
      return [
        `Current weather in ${city}: ${weatherData.temperature.celsius}°C, ${weatherData.conditions}`,
        weatherData,
      ];
    },
    {
      name: 'get_weather',
      description: 'Fetches weather information and returns structured data.',
      inputSchema: z.toJSONSchema(weatherSchema),
      validate: (input) => weatherSchema.parse(input),
      responseFormat: 'content_and_artifact',
    }
  );
};

// Use the tool with inline assistant
export function WeatherComponent() {
  const [result, setResult] = useState('');
  const [weatherData, setWeatherData] = useState<WeatherData | null>(null);
  const gen = useInlineAssistant();

  const handleGetWeather = () => {
    gen.generate({
      prompt: 'What is the weather in San Francisco?',
      origin: 'my-plugin/weather',
      tools: [createWeatherTool((data) => setWeatherData(data))], // Pass callback
      onComplete: (text) => setResult(text),
      onError: (err) => console.error(err),
    });
  };

  return (
    <div>
      <Button onClick={handleGetWeather} disabled={gen.isGenerating}>
        Get Weather
      </Button>
      {result && <div>{result}</div>}
      {weatherData && <pre>{JSON.stringify(weatherData, null, 2)}</pre>}
    </div>
  );
}
```

### Investigation Events

Subscribe to investigation lifecycle events to react when investigations are created.

#### `onInvestigationCreated(callback: (payload: InvestigationCreatedPayload) => void): () => void`

Subscribe to investigation created events.

**Parameters:**

- `callback` - Function called when an investigation is created, receives `{ investigationId: string }`

**Returns:** An unsubscribe function to stop listening for events

**Example:**

```typescript
import { onInvestigationCreated } from '@grafana/assistant';

// Subscribe to investigation creation events
const unsubscribe = onInvestigationCreated((payload) => {
  console.log('Investigation created:', payload.investigationId);
  // Navigate to the investigation, update UI, etc.
});

// Later, when you want to stop listening
unsubscribe();
```

#### `emitInvestigationCreated(investigationId: string): void`

Emit an investigation created event. This is typically used internally by the Assistant but can be called by external integrations if needed.

**Parameters:**

- `investigationId` - The ID of the newly created investigation

### Availability Functions

#### `isAssistantAvailable(): Observable<boolean>`

Checks if the assistant plugin is available.

**Returns:** An RxJS Observable that emits `true` if the assistant is available, `false` otherwise.

### Terms and Conditions Functions

> **Note:** In most cases, plugins do not need to check terms directly. The Grafana Assistant automatically handles cases when terms are not accepted by prompting the user to accept them before proceeding.

#### `checkTerms(): Promise<boolean>`

Checks whether terms and conditions are accepted and up-to-date. This function verifies that:

1. Terms have been accepted
2. The accepted version matches or exceeds the current version

**Returns:** A promise that resolves to `true` if terms are accepted and up-to-date, `false` otherwise.

**Throws:** Error if the API call fails.

**Example:**

```typescript
import { checkTerms } from '@grafana/assistant';

try {
  const accepted = await checkTerms();
  if (!accepted) {
    // Show terms acceptance UI
  }
} catch (error) {
  // Handle error
  console.error('Failed to check terms:', error);
}
```

### Sidebar Functions

#### `openAssistant(props: OpenAssistantProps): void`

Opens the Grafana Assistant sidebar.

**Parameters:**

- `props: OpenAssistantProps` - Configuration object
  - `origin: string` - Origin of the request that opened the assistant. Should be structured using forward slashes with the first part as a namespace. Examples: 'grafana-datasources/prometheus/query-builder', 'grafana-slo-app/slo-editor-overview'
  - `prompt?: string` - Optional initial prompt to display in the assistant
  - `context?: ChatContextItem[]` - Optional context items to provide additional information to the assistant
  - `autoSend?: boolean` - Optional flag to automatically send the initial prompt

#### `closeAssistant(): void`

Closes the Grafana Assistant sidebar.

### Context Functions

#### `createAssistantContextItem<T>(type: T, params: ContextTypeParams<T>): ChatContextItem`

Creates a context item that can be passed to the assistant to provide additional information.

**Parameters:**

- `type` - The type of context to create. Supported types:
  - `'datasource'` - Datasource context
  - `'dashboard'` - Dashboard context
  - `'dashboard_folder'` - Folder context
  - `'label_name'` - Label name context
  - `'label_value'` - Label value context
  - `'structured'` - Custom structured data context
  - `'component'` - Custom React components context
  - `'unknown'` - Generic text context
- `params` - Parameters specific to the context type (see Context Types section). All context types support:
  - `hidden?: boolean` - If true, the context item will not be shown in the UI pills but will still be sent to the assistant. This is useful for providing system instructions, behavioral guidelines, or other metadata that shouldn't clutter the user interface.

**Returns:** A `ChatContextItem` that can be passed to `openAssistant`

#### `providePageContext(urlPattern: string | RegExp, initialContext: ChatContextItem[]): ((context: ChatContextItem[]) => void) & { unregister: () => void }`

Registers context items for specific pages based on URL patterns. Returns a setter function to update the context dynamically. The context will be automatically included when the assistant is opened on pages matching the pattern.

**Parameters:**

- `urlPattern` - URL pattern (string with wildcards or RegExp) to match against page URLs
  - String patterns support `*` (match any characters) and `**` (match any path segments)
  - RegExp patterns provide full regex matching capabilities
- `initialContext` - Initial array of `ChatContextItem` to provide when the pattern matches

**Returns:** A setter function to update the context, with an `unregister` method attached for cleanup

**Examples:**

```typescript
// String patterns - returns setter function
const setDashboardContext = providePageContext('/d/*', initialContext); // Match any dashboard
const setExploreContext = providePageContext('/explore**', initialContext); // Match explore and subpaths

// RegExp patterns
const setSpecificContext = providePageContext(/^\/d\/[a-zA-Z0-9]+$/, initialContext); // Match specific dashboard format

// Update context dynamically
setDashboardContext([...newContext]);

// Cleanup when done
setDashboardContext.unregister();
```

#### `provideComponents(prompt: string, namespace: string, components: Record<string, ComponentImplementation>, urlPattern?: string | RegExp): ((context: ChatContextItem[]) => void) & { unregister: () => void }`

A simplified function for providing components to the assistant. This is a convenience wrapper around `providePageContext` specifically for registering components.

**Parameters:**

- `prompt` - Instructions for the assistant on how and when to use these components
- `namespace` - A unique identifier for your app's components (e.g., 'myapp', 'datasource')
- `components` - A record of component names to their React component implementations
- `urlPattern` - Optional URL pattern to match against page URLs (defaults to `/.*/` - matches all pages)

**Returns:** A setter function to update the context, with an `unregister` method attached for cleanup

**Example:**

```typescript
import { provideComponents } from '@grafana/assistant';
import MyEntityMention from './components/MyEntityMention';
import MyCustomChart from './components/MyCustomChart';

// Register components for all pages
const setComponents = provideComponents(
  `
  - When mentioning entities, use the myapp_MyEntityMention component: <myapp_MyEntityMention name={'entity-name'} type={'entity-type'} />
  - For charts, use the myapp_MyCustomChart component: <myapp_MyCustomChart data={chartData} title={'Chart Title'} />
  - Custom components must never be wrapped in code blocks.
  `,
  'myapp',
  {
    MyEntityMention,
    MyCustomChart,
  }
);

// Register components only for dashboard pages
const setDashboardComponents = provideComponents(
  'Use components to enhance dashboard analysis.',
  'dashboard',
  { MyEntityMention },
  '/d/*'
);

// Cleanup when done
setComponents.unregister();
```

### Questions System

The questions system allows external parties to provide sample prompts with optional context for specific pages. It reuses the existing page context infrastructure but presents a simpler interface focused on questions.

#### `provideQuestions(urlPattern: string | RegExp, initialQuestions: Question[]): ((questions: Question[]) => void) & { unregister: () => void }`

Registers questions for specific pages based on URL patterns. Returns a setter function to update the questions dynamically.

**Parameters:**

- `urlPattern` - URL pattern (string with wildcards or RegExp) to match against page URLs
- `initialQuestions` - Initial array of `Question` to provide when the pattern matches

**Returns:** A setter function to update the questions, with an `unregister` method attached for cleanup

**Examples:**

```typescript
// Register questions for dashboard pages
const setQuestions = provideQuestions('/d/*', [
  {
    prompt: 'What metrics are available in this dashboard?',
    context: [], // Optional context items created with `createAssistantContextItem`
  },
  {
    prompt: 'How can I optimize the queries in this dashboard?',
    context: [
      createAssistantContextItem('dashboard', {
        dashboardUid: 'dashboard-uid',
        dashboardTitle: 'System Overview',
        folderUid: 'folder-uid',
        folderTitle: 'Production',
      }),
    ],
  },
]);

// Update questions dynamically
setQuestions([
  {
    prompt: 'What are the key insights from this dashboard?',
    context: [],
  },
]);

// Cleanup when done
setQuestions.unregister();
```

#### `useProvideQuestions(urlPattern: string | RegExp, initialQuestions?: Question[]): (questions: Question[]) => void`

React hook for providing questions that automatically cleans up on unmount. This is the recommended way to use questions in React components.

**Parameters:**

- `urlPattern` - URL pattern (string or RegExp) to match against page URLs
- `initialQuestions` - Initial array of questions to provide when the pattern matches (defaults to empty array)

**Returns:** A setter function to update the questions

**Example:**

```typescript
function DashboardComponent() {
  const setQuestions = useProvideQuestions('/d/*', [
    {
      prompt: "What does this dashboard show?",
    },
    {
      prompt: "How can I improve this dashboard?",
    }
  ]);

  return <div>Dashboard</div>;
}
```

#### `useQuestions(): Question[]`

React hook to get all questions that match the current URL. This filters the page context to only return question-type items.

**Returns:** Array of questions from all matching registrations

**Example:**

```typescript
function AssistantComponent() {
  const questions = useQuestions();

  return (
    <div>
      <h3>Suggested Questions:</h3>
      {questions.map((question, index) => (
        <div key={index}>
          <p>{question.prompt}</p>
          {question.context && question.context.length > 0 && (
            <p>Has additional context</p>
          )}
        </div>
      ))}
    </div>
  );
}
```

### Sidebar Types

#### `OpenAssistantProps`

```typescript
type OpenAssistantProps = {
  prompt?: string;
  context?: ChatContextItem[];
  autoSend?: boolean;
};
```

Configuration object for opening the assistant.

- `prompt` - Optional initial prompt to display
- `context` - Optional context items to provide
- `autoSend` - Optional flag to automatically send the initial prompt

#### `ChatContextItem`

```typescript
type ChatContextItem = {
  node: TreeNode;
  occurrences: string[];
};
```

A context item that provides structured information to the assistant.

### Page Context Types

#### `PageContextRegistration`

```typescript
interface PageContextRegistration {
  id: string;
  urlPattern: string | RegExp;
  context: ChatContextItem[];
}
```

Represents a registered page context mapping.

### Questions Types

#### `Question`

```typescript
interface Question {
  prompt: string; // The sample prompt/question
  context?: ChatContextItem[]; // Optional context items
}
```

Represents a question with an optional prompt and context.

#### `QuestionRegistration`

```typescript
interface QuestionRegistration {
  id: string;
  urlPattern: string | RegExp;
  questions: Question[];
}
```

Represents a registered questions mapping.

### Context Types

#### Datasource Context Parameters

```typescript
interface CreateDatasourceContextParams {
  datasourceUid: string;
  title?: string;
  icon?: IconName;
  hidden?: boolean; // If true, the context item will not be shown in the context pills
}
```

#### Dashboard Context Parameters

```typescript
interface CreateDashboardContextParams {
  dashboardUid: string;
  dashboardTitle: string;
  folderUid?: string;
  folderTitle?: string;
  title?: string;
  icon?: IconName;
  hidden?: boolean; // If true, the context item will not be shown in the context pills
}
```

#### Folder Context Parameters

```typescript
interface CreateFolderContextParams {
  folderUid: string;
  folderTitle: string;
  title?: string;
  icon?: IconName;
  hidden?: boolean; // If true, the context item will not be shown in the context pills
}
```

#### Label Name Context Parameters

```typescript
interface CreateLabelNameContextParams {
  datasourceUid: string;
  datasourceType: string;
  labelName: string;
  title?: string;
  icon?: IconName;
  hidden?: boolean; // If true, the context item will not be shown in the context pills
}
```

#### Label Value Context Parameters

```typescript
interface CreateLabelValueContextParams {
  datasourceUid: string;
  datasourceType: string;
  labelName: string;
  labelValue: string;
  title?: string;
  icon?: IconName;
  hidden?: boolean; // If true, the context item will not be shown in the context pills
}
```

#### Structured Data Context Parameters

```typescript
interface StructuredNodeDataParams {
  data: Record<string, any>;
  title?: string;
  icon?: IconName;
  hidden?: boolean; // If true, the context item will not be shown in the context pills
}
```

#### Component Context Parameters

```typescript
interface CreateComponentContextParams {
  components: Record<string, React.ComponentType<any>>;
  prompt: string;
  namespace?: string; // Optional: defaults to 'components' if not provided
  title?: string;
  icon?: IconName;
  hidden?: boolean; // If true, the context item will not be shown in the context pills
}
```

#### Generic Context Parameters

```typescript
interface NodeDataParams {
  id: string;
  text?: string;
  title?: string;
  icon?: IconName;
  hidden?: boolean; // If true, the context item will not be shown in the context pills
}
```

### Function Extension Types

#### `CallbackFunction`

```typescript
type CallbackFunction = () => FunctionNamespace[];
```

A function that returns an array of function namespaces.

#### `FunctionImplementation`

```typescript
type FunctionImplementation = (...args: any[]) => any;
```

A type for function implementations that can be exposed to the assistant.

#### `NamedFunctions`

```typescript
type NamedFunctions = Record<string, FunctionImplementation>;
```

A record of function names to their implementations.

#### `FunctionNamespace`

```typescript
type FunctionNamespace = {
  namespace: string;
  functions: NamedFunctions;
};
```

Groups functions under a namespace.

### Utility Functions

#### `newFunctionNamespace(namespace: string, functions: NamedFunctions): FunctionNamespace`

Creates a new function namespace.

**Parameters:**

- `namespace: string` - The namespace identifier
- `functions: NamedFunctions` - The functions to include in the namespace

#### `getExposeAssistantFunctionsConfig(namespaces: FunctionNamespace[]): PluginExtensionAddedFunctionConfig`

Creates a plugin extension configuration for exposing functions to the assistant.

**Parameters:**

- `namespaces: FunctionNamespace[]` - Array of function namespaces to expose

### Constants

#### `CALLBACK_EXTENSION_POINT`

```typescript
const CALLBACK_EXTENSION_POINT = 'grafana-assistant-app/callback/v0-alpha';
```

The extension point ID for registering assistant functions.

### Additional Exported Types

The package also exports the following types for advanced use cases:

```typescript
// Data types for context items
export type DashboardNodeData;
export type DatasourceNodeData;
export type FolderNodeData;
export type LabelNameNodeData;
export type LabelValueNodeData;
export type StructuredNodeData;
export type TreeNode;
export type ItemDataType;

// Component types
export type ComponentImplementation;
export type NamedComponents;
```

## License

Apache-2.0