Aller au contenu

Applications

Applications are custom frontend interfaces that can be uploaded and integrated into Forra assistants. They provide a tailored user experience for specific workflows while leveraging Forra's backend capabilities through the Forra Chat SDK.

Quick Start

The fastest way to get started is with the Forra CLI:

forracli apps init -d my-app
cd my-app/micro-app
bun install
forracli apps deploy -f ../assistants.json
make dev

Open Forra and find your created assistant - the template app replaces the chat interface.

Production Deployment: Remove the ui_url (dev) section from assistants.json when ready for other users.

Advanced Overview

An Application is essentially a web application that: - Uses the @mirego/forra-api SDK to interact with Forra's API - Uses the @mirego/forra-react SDK to use Forra's reusable components - Can create and manage conversations (sessions) - Can read and write user data for session persistence - Can execute custom functions defined in the assistant

Core Concepts

Access Token

When an Application is loaded within Forra, it automatically receives an access token that provides access to: - Conversation Management: Create new conversations and manage existing ones - User Data: Store and retrieve custom session information - Custom Functions: Execute backend functions uploaded to the assistant

Session Management

Each Application instance can run within a conversation context, allowing you to: - Persist state across user interactions - Track progress through multi-step workflows - Store custom data specific to the session

Building from Scratch

1. Setup Your Project

Create a web application using any framework that outputs an index.html-based bundle. React with Vite is recommended.

2. Wrap Your App with ScoutApp

import { ScoutApp } from '@mirego/forra-react';
import { StrictMode } from 'react';
import { createRoot } from 'react-dom/client';
import App from './App.tsx';

createRoot(document.getElementById('root')!).render(
  <StrictMode>
    <ScoutApp>
      <App />
    </ScoutApp>
  </StrictMode>
);

3. Access Forra Context

import { ScoutAppContext } from '@mirego/forra-react';
import { useContext } from 'react';

const App = () => {
  const { 
    conversation_id, 
    assistant_id, 
    language,
    redirectToNewConversation,
    onConversationCreated,
    redirectToConversation 
  } = useContext(ScoutAppContext);

  // Your app logic here
  return <div>Your application content</div>;
};

Key SDK Functions

Conversation Management

Create a New Conversation

import { createConversation } from '@mirego/forra-react';

const newConversation = await createConversation({
  title: 'My Workflow Session',
  payload: [],
  assistant_id,
  user_data: { step: 'initial', customData: 'value' }
});

Query Conversation Data

import { useConversationQuery } from '@mirego/forra-react';

const conversationQuery = useConversationQuery({
  conversationId: conversation_id,
  refetchInterval: 1000 // Poll every second if needed
});

const userData = conversationQuery.data?.user_data;

Update User Data

import { updateConversationUserData } from '@mirego/forra-api';

await updateConversationUserData(conversation_id, {
  step: 'processing',
  progress: 50,
  customField: 'updated value'
});

Update a Single User Data Key

import { updateConversationUserDataKey } from '@mirego/forra-api';

await updateConversationUserDataKey(conversation_id, 'step', 'processing');

Only the specified key is updated — other keys in user_data are left unchanged.

Execute Custom Functions

import { executeAssistantCustomFunction } from '@mirego/forra-api';

await executeAssistantCustomFunction(
  assistant_id,
  'my_custom_function_name',
  { 
    input_parameter: 'value',
    another_param: 123 
  },
  conversation_id
);

File Handling

import { 
  getSignedUploadUrl, 
  uploadFile, 
  fetchSignedUrl 
} from '@mirego/forra-api';

// Upload a file
const uploadUrl = await getSignedUploadUrl(filename, filesize);
await uploadFile(uploadUrl.data.url, file);

// Download a file
const downloadUrl = await fetchSignedUrl(protectedFilePath);
const response = await axios.get(downloadUrl.data.url, { 
  responseType: 'blob' 
});

UI Components

The SDK provides pre-built UI components that follow Forra's design system:

import {
  MicroAppHeader,
  MicroAppNewConversationButton,
  MicroAppStepsContainer,
  MicroAppStep,
  MicroAppFileUpload,
  MicroAppFileInfo,
  Button,
  Input
} from '@mirego/forra-react';

// Header with logo and title
<MicroAppHeader 
  AppLogo={() => <img src="your-logo.png" />} 
  title="My Application" 
  subtitle="Description of functionality" 
/>

// Step-based workflow
<MicroAppStepsContainer>
  <MicroAppStep
    state="completed" // 'idle' | 'loading' | 'completed' | 'fail'
    title="Step 1"
    errorMessage="Error message if failed"
  >
    Step content here
  </MicroAppStep>
</MicroAppStepsContainer>

// File operations
<MicroAppFileUpload onUpload={handleFileUpload} />
<MicroAppFileInfo 
  filename="output.txt" 
  onDownload={handleDownload} 
/>

Example Implementation

Here's a simplified example of an application that processes a URL through multiple steps:

import { 
  ScoutAppContext, 
  useConversationQuery,
  createConversation,
  executeAssistantCustomFunction,
  MicroAppHeader,
  MicroAppStepsContainer,
  MicroAppStep
} from '@mirego/forra-react';
import { useContext, useState, useEffect } from 'react';

interface UserData {
  step: 'idle' | 'processing' | 'completed';
  inputUrl?: string;
  result?: string;
  error?: string;
}

const App = () => {
  const { conversation_id, assistant_id, redirectToNewConversation } = 
    useContext(ScoutAppContext);
  const [inputUrl, setInputUrl] = useState('');

  const conversationQuery = useConversationQuery({
    conversationId: conversation_id,
    refetchInterval: conversation_id ? 1000 : undefined
  });

  const userData = conversationQuery.data?.user_data as UserData;

  const processUrl = async () => {
    const conversation = await createConversation({
      title: `Processing ${inputUrl}`,
      payload: [],
      assistant_id,
      user_data: { step: 'processing', inputUrl }
    });

    await executeAssistantCustomFunction(
      assistant_id,
      'process_url_function',
      { url: inputUrl },
      conversation.data.id
    );
  };

  return (
    <div>
      {conversation_id && (
        <MicroAppNewConversationButton 
          onClick={redirectToNewConversation} 
        />
      )}

      <MicroAppHeader 
        title="URL Processor" 
        subtitle="Process URLs with AI" 
      />

      {!userData || userData.step === 'idle' ? (
        <div>
          <input 
            value={inputUrl}
            onChange={(e) => setInputUrl(e.target.value)}
            placeholder="Enter URL"
          />
          <button onClick={processUrl}>Process</button>
        </div>
      ) : (
        <MicroAppStepsContainer>
          <MicroAppStep
            state={userData.step === 'processing' ? 'loading' : 'completed'}
            title="Processing URL"
            errorMessage={userData.error}
          >
            {userData.result && <p>{userData.result}</p>}
          </MicroAppStep>
        </MicroAppStepsContainer>
      )}
    </div>
  );
};