Skip to content
Tags

PDF

Integrate LLM services

Required Hotfix for Version 3.7.0

It has been determined that hotfix hotFix_ANS_370_004 is required for the LLM service to work properly on Module Suite version 3.7.0. Please ensure this hotfix is applied to your system before using the LLM service. Failure to apply this hotfix may result in unexpected behavior or errors when using LLM-related features.

To obtain and apply this hotfix, please contact AnswerModules support or refer to the official documentation for hotfix installation procedures.

Integrate Large Language Models in your workflow

Introduction

Large Language Models (LLMs) are revolutionizing the way organizations process and leverage information. These sophisticated AI models, trained on vast amounts of textual data, can understand, generate, and manipulate human-like text with remarkable accuracy. As businesses increasingly deal with enormous volumes of unstructured data, integrating LLMs into existing workflows has become a game-changer for enhancing productivity, improving decision-making processes, and unlocking new insights.

Module Suite plays a crucial role in seamlessly incorporating LLMs into your enterprise content management ecosystem. By bridging the gap between your organization's content repositories and cutting-edge AI capabilities, Module Suite empowers you to:

  1. Automate content classification: Leverage LLMs to automatically categorize and classify documents, making information retrieval faster and more accurate.

  2. Enhance search functionality: Utilize natural language processing to improve search results, allowing users to find relevant information using conversational queries.

  3. Generate intelligent summaries: Create concise summaries of lengthy documents, enabling quick understanding of key points without manual review.

  4. Streamline content creation: Assist users in drafting documents, emails, and reports by providing AI-powered suggestions and completions.

  5. Facilitate knowledge discovery: Uncover hidden patterns and relationships within your content, leading to valuable insights for decision-makers.

  6. Improve data extraction: Extract relevant information from unstructured documents, making it easier to populate structured databases or forms.

By integrating LLMs through Module Suite, organizations can harness the power of AI to transform their content management processes, leading to increased efficiency, reduced manual effort, and improved overall productivity.

LLM Integration Considerations

While LLMs offer significant benefits, it's important to consider factors such as data privacy, model selection, and fine-tuning requirements when integrating them into your workflow. Module Suite provides the necessary tools and interfaces to address these considerations effectively.

Architecture and Networking

Module Suite acts as a central hub for communication between Extended ECM (xECM), various LLM API services, and local resources.

Here's an overview of how the networking and communication work:

flowchart TD
    subgraph ECM["Extended ECM (xECM)"]
        MS[Module Suite]
    end

    API1[OpenAI API]
    API2[Azure AI API]
    API3[Ollama API]

    MS <--> |Internal APIs| ECM
    MS <--> |HTTP/REST| API1
    MS <--> |HTTP/REST| API2
    MS <--> |HTTP/REST| API3

    style ECM fill:#f9f,stroke:#333,stroke-width:2px
    style MS fill:#bbf,stroke:#333,stroke-width:2px
    style API1 fill:#bfb,stroke:#333,stroke-width:2px
    style API2 fill:#bfb,stroke:#333,stroke-width:2px
    style API3 fill:#bfb,stroke:#333,stroke-width:2px

Integration with xECM

Module Suite runs directly on xECM, providing seamless access to all xECM APIs. This tight integration allows for efficient data exchange and leveraging of xECM's content management capabilities.

LLM API Communication

Module Suite implements independent communication channels to various LLM API providers, which can be: - Public internet services (e.g., OpenAI) - VPN-accessible services (e.g., Azure AI) - On-premises solutions (e.g., LLAMA3 using Ollama to expose an API)

OpenAI-Compatible API Privileged Support

Module Suite features a rich API specifically designed for OpenAI-compatible API service providers, most commonly used with OpenAI and Azure. This allows for flexible integration with different LLM services while maintaining a consistent interface.

Local Embedding Indexes

To enhance performance and maintain data control, Module Suite allows administrators to configure and create local embedding indexes. These indexes are typically used for implementing Retrieval-Augmented Generation (RAG) systems. Key points include:

  • Based on an adapted version of the Lucene open-source indexing engine
  • Requires sending text chunks to the LLM API service provider for embedding computation
  • Does not store entire documents outside your organization
  • Provides full control over chunking policies and methodologies

Permission Considerations

When implementing local embedding indexes, it's crucial to ensure that permissions are properly considered. This helps maintain data security and access control in line with your organization's policies. We will explore this in detail in the following sections.

Typical Communication Sequence

Below is a diagram illustrating a typical communication sequence when using Module Suite with xECM and an LLM API service for implementing a RAG:

sequenceDiagram
    participant User
    participant xECM
    participant Module Suite UI Widget
    participant Module Suite (Script Engine)
    participant LocalIndex
    participant LLMAPI

    User->>xECM: Request content
    xECM->>Module Suite UI Widget: Pass request
    Module Suite UI Widget->>Module Suite (Script Engine): Pass request and history
    Module Suite (Script Engine)->>LocalIndex: Query local index
    LocalIndex-->>Module Suite (Script Engine): Return relevant chunks coordinates
    Module Suite (Script Engine)->>xECM: Retrive relevant chunks (context)
    xECM-->>Module Suite (Script Engine): Returns relevant chunks
    Module Suite (Script Engine)->>LLMAPI: Send prompt with context
    LLMAPI-->>Module Suite (Script Engine): Return LLM response
    Module Suite (Script Engine)->>Module Suite UI Widget: Process and format response
    Module Suite UI Widget->>xECM: Render result
    xECM->>User: Display result

Service Provider Support in Module Suite

Module Suite offers extensive support for various LLM API providers, with a focus on OpenAI-compatible APIs and limited support for other providers. Below is a detailed breakdown of the supported features for each provider type.

OpenAI API Providers (OpenAI and Microsoft Azure AI)

Module Suite provides comprehensive support for OpenAI-compatible APIs, including those from OpenAI itself and Microsoft Azure AI. The following features are supported:

  1. Chat Completion
  2. Text Completion
  3. Function Invocation
  4. Vision (image analysis and processing)
  5. Text-to-Speech
  6. Speech-to-Text
  7. Assistant API
  8. Embeddings
  9. Fine-tuning
  10. Moderation

This wide range of supported features allows for versatile integration of AI capabilities into your Extended ECM workflows, enabling tasks from simple text generation to complex multimodal interactions.

Ollama API Support

For Ollama-based API providers, Module Suite currently offers limited but essential support:

  1. Embeddings
  2. Chat Completion

While more restricted than the OpenAI API support, these features still allow for crucial functionalities such as semantic search and conversational AI interactions using on-premises or self-hosted models.

graph TD
    A[Module Suite API Support] --> B[OpenAI APIs]
    A --> C[Ollama APIs]

    B --> D[Chat Completion]
    B --> E[Text Completion]
    B --> F[Function Invocation]
    B --> G[Vision]
    B --> H[Text-to-Speech]
    B --> I[Speech-to-Text]
    B --> J[Assistant API]
    B --> K[Embeddings]
    B --> L[Fine-tuning]
    B --> M[Moderation]

    C --> N[Embeddings]
    C --> O[Chat Completion]

    style B fill:#f9f,stroke:#333,stroke-width:2px
    style C fill:#bbf,stroke:#333,stroke-width:2px
    style N stroke:#333,stroke-width:2px
    style O stroke:#333,stroke-width:2px

API Support Evolution

The landscape of LLM APIs is rapidly evolving. Module Suite's API support is regularly updated to include new providers and features. Always refer to the latest documentation for the most up-to-date information on supported APIs and features.

Choosing the Right API Provider

When selecting an API provider for your Module Suite implementation, consider the following factors:

  • Feature requirements: Assess which AI capabilities are crucial for your use case.
  • Data privacy and compliance: Determine if you need to keep data on-premises or if cloud-based solutions are acceptable.
  • Performance needs: Evaluate the response times and throughput required for your applications.
  • Cost considerations: Compare pricing models of different providers, especially for high-volume usage.
  • Integration complexity: Consider the ease of integration with your existing infrastructure.

Components of the LLM Service Integration

Module Suite provides a comprehensive set of components to enable seamless integration with LLM services. These components work together to offer a robust and flexible AI-enhanced experience within the Extended ECM environment. Let's explore each of these components:

Content Script

Module Suite features a set of dedicated extension package to support LLM API integration.

OpenAI Extension Package Service

The OpenAI service is a dedicated Content Script extension package service specifically designed for OpenAI-compatible API integrations. Key features include:

  • Multi-profile support for flexible configuration
  • Comprehensive implementation of OpenAI's API features
  • Optimized for use with OpenAI and Azure AI services

LLM Extension Package Service

The LLM service is a more general-purpose Content Script extension package service for integrating various LLM providers. Its features include:

  • Multi-profile configuration for supporting different LLM services
  • Currently focused on Ollama API support
  • Extensible architecture for future LLM provider integrations

Widgets

Smart Pages Widget (named CARL)

This Smart Pages widget brings AI-powered capabilities directly into the Extended ECM user interface. This widget:

  • Provides an interactive AI assistant interface within Smart Pages
  • Leverages the power of LLM models for various tasks
  • Enhances user productivity by offering AI-assisted functionalities

Beautiful WebForm Widget (named CARL)

This Beautiful WebForm Widget extends AI capabilities to WebForms, allowing for:

  • AI-enhanced form interactions
  • Intelligent form filling assistance
  • Dynamic content generation based on form context

Services

Content Script Service (named carl)

This Content Script Service acts as the backend engine for LLM-related functionalities, providing:

  • Integration between widgets and LLM services
  • Business logic for processing AI requests and responses
  • Customizable workflows for AI-assisted operations

Code Snippets

Content Script Snippets

Module Suite includes several Content Script snippets that:

  • Facilitate quick implementation of common LLM-related tasks
  • Provide reusable code for developers to extend AI functionalities
  • Demonstrate best practices for integrating AI capabilities into Content Scripts

CARL (Content Server Artificial intelligence Resource and Liaison)

CARL is a feature of Module Suite, introduced with version 3.5, that implements a Content Script co-pilot based on the integration with GPT family models.

CARL Integration in Content Script Editor

When enabled in the Base Configuration, CARL integrates directly into the Content Script editor, offering:

  • AI-assisted code completion and suggestions
  • Context-aware help and documentation
  • Intelligent debugging assistance

CARL Beta Status

CARL is currently in beta. As a beta feature, it may undergo changes and improvements in future releases. Users are encouraged to provide feedback to help shape its development.

graph TD
    A[Module Suite] --> B[OpenAI Service]
    A --> C[LLM Service]
    A --> D[LLM Integration Features]
    D --> E[Smart Pages Widget CARL]
    D --> F[Beautiful WebForm Widget CARL]
    D --> G[Content Script Service carl]
    A --> H[Content Script Snippets]
    A --> I[CARL Co-pilot Feature]
    I --> J[Content Script Editor Integration]

    B --> K[OpenAI/Azure AI]
    C --> L[Ollama]

    style A fill:#f9f,stroke:#333,stroke-width:2px
    style D fill:#bbf,stroke:#333,stroke-width:2px
    style I fill:#fbb,stroke:#333,stroke-width:2px
    style B stroke:#333,stroke-width:2px
    style C stroke:#333,stroke-width:2px

Integration Use Cases

Module Suite offers various capabilities for integrating AI-powered functionalities into your Extended ECM environment. Let's explore common use cases and how to implement them.

Chat Completion

Chat completion allows you to create interactive, context-aware conversations with an AI assistant. This functionality is valuable for implementing:

  • Intelligent chatbots for user support
  • Virtual assistants for guided ECM operations
  • Interactive help systems within your ECM applications
  • Natural language interfaces for complex queries or tasks

Example: Basic Chat Interaction

Here's a simple example of how to implement a chat completion interaction:

def systemPreamble = """You are C.A.R.L. (Content Server Artificial intelligence Resource and Liaison), an LLM designed to help users working with OpenText Extended ECM"""
def defaultTemperature = 0.7
def model = "gpt-4"
def maxTokens = 2000

try {
    // Our services are implemented using fluent APIs and builders to simplify their usage
    // Use the auto-completion feature of the editor (CTRL+Space) to explore available configuration options
    def reqBuilder = openai.newChatCompletionRequestBuilder()
        .model(model)
        .temperature(defaultTemperature)
        .maxTokens(maxTokens)
        .n(1) // Request only one completion (most common use case)

    def req = reqBuilder.build()

    // Instruct the agent about its purpose and constraints using a system message
    req.addChatMessage("system", systemPreamble)

    // Add user request or message
    req.addChatMessage("user", "Does xECM support the concept of metadata?")

    // Submit the request and synchronously wait for the response
    def result = openai.createChatCompletion(req)

    // Extract and output the content of the first (and only) choice
    out << result.choices[0].message.content
} catch (Exception e) {
    out << "An error occurred: " + e.getMessage()
}

This code configures the request with specific parameters such as the model to use (GPT-4), temperature setting for response randomness, and maximum token limit. The system message defines C.A.R.L.'s role, followed by a user question about xECM's metadata support. The openai.newChatCompletionRequestBuilder() method initializes the request, which is then configured and built. The addChatMessage() method is used to add both system and user messages to the conversation. Finally, the createChatCompletion() method sends the request to the OpenAI API and retrieves the response.

This example showcases the ease of use of the OpenAI service in Module Suite, allowing developers to quickly implement AI-powered chat functionalities within their Extended ECM environment. The service handles the complexities of API interaction, allowing developers to focus on crafting effective prompts and integrating the responses into their applications.

Use Autocompletion

Remember to use the auto-completion feature of the editor (CTRL+Space) to explore available configuration options when working with the request builder.

def systemPreamble = """You are C.A.R.L. (Content Server Artificial intelligence Resource and Liaison), an LLM designed to help users working with OpenText Extended ECM"""
def defaultTemperature = 0.7
def model = "llama3"
def maxTokens = 2000
def msgID = "MyMessage"

try {
    // Use the llm service with a LangChain-based builder for Ollama
    def reqBuilder = llm.newLangChainChatCompletionRequestBuilder("ollama")
        .model(model)
        .temperature(0.0)
        .logRequestsAndResponses(true)

    // Set a custom timeout for non-OpenAI services
    reqBuilder.builder.timeout(java.time.Duration.ofSeconds(180))

    def req = reqBuilder.build()

    // Add system message to define C.A.R.L.'s role
    req.addChatMessage("system", systemPreamble)

    // Add user query
    req.addChatMessage("user", "Does xECM support the concept of metadata?")

    // Submit the request and wait for the response
    def result = llm.createChatCompletion(req)

    // Output the AI-generated response
    out << result.choices[0].message.content
} catch (Exception e) {
    out << "An error occurred: " + e.getMessage()
}

This example illustrates the flexibility of Module Suite's AI integration.

Key points to note:

  • The llm service is used instead of a specific provider service, allowing for more generic implementations.
  • The newLangChainChatCompletionRequestBuilder method is used with "ollama" as the provider.
  • LangChain is utilized for integration with non-OpenAI services, offering additional configuration options like custom timeouts.
  • The overall structure of setting up the request, adding messages, and retrieving the response remains similar to the previous example.

By using the generic llm service, you can easily switch between different AI providers or models while maintaining a consistent implementation structure. This flexibility allows you to choose the most suitable AI backend for your specific use case or requirements.

Different models different results

Experiment with different models and providers to find the best balance of performance, cost, and capabilities for your ECM AI integration needs.

Chat Completion (continued)

Example: Streaming Chat Completion

Streaming chat completion allows for real-time delivery of AI-generated responses, enhancing the responsiveness of your AI-powered applications. This is particularly useful for:

  • Creating more interactive and dynamic user experiences
  • Implementing typing-like effects in chatbots
  • Handling long-form content generation without waiting for the entire response

Here's an example of how to implement streaming chat completion:

def systemPreamble = """You are C.A.R.L. (Content Server Artificial intelligence Resource and Liaison), an LLM designed to help users working with OpenText Extended ECM"""
def defaultTemperature = 0.7
def model = "gpt-4"
def maxTokens = 2000
def msgID = "MyMessage"

try {
    def reqBuilder = openai.newChatCompletionRequestBuilder()
        .model(model)
        .temperature(defaultTemperature)
        .n(1)

    def req = reqBuilder.build()
    req.addChatMessage("system", systemPreamble)
    req.addChatMessage("user", "Does xECM support the concept of metadata?")

    def iterator = 0
    result = openai.streamChatCompletion(req, { block ->
        // This closure is invoked with streaming chunks as they become available
        def map = [
            content: block.choices?[0]?.message?.content,
            role:    block.choices?[0]?.message?.role,
            finishReason: (block.choices?[0]?.message?.content != null) ? block.choices?[0]?.finishReason : 'stop'
        ]
        log.debug("GOT {}", map) // Log for debugging
        cache.put(msgID + "_" + iterator++, 500, map)
    })

    out << result.choices
} catch (Exception e) {
    out << "An error occurred: " + e.getMessage()
}

Key points about this streaming implementation:

The streamChatCompletion method is used instead of createChatCompletion. A closure is provided as the second argument to streamChatCompletion. This closure is called for each chunk of the response as it becomes available. The closure implements a producer-consumer pattern:

It acts as the producer, storing each chunk in the memcache service. The UI can act as the consumer, retrieving chunks from the memcache to display in real-time.

Each chunk is stored in the memcache with a unique key combining the msgID and an incrementing iterator. The finishReason is tracked to determine when the response is complete.

This approach allows for more responsive AI interactions, as the UI can start displaying the response before it's fully generated. It's particularly useful for longer responses or when you want to create a more dynamic, "typing" effect in your AI interface.

Use debouncing

When implementing the UI for streaming responses, consider using techniques like debouncing to balance between real-time updates and performance.

Additional considerations: Producer-Consumer Pattern

The streaming chat completion implementation uses a producer-consumer pattern to handle real-time data flow. Here's a sequence diagram illustrating this process:

Default Consumer Service

A default implementation of the consumer service, named "carl", is provided as a Content Script Service.

You can find it at: Content Script Volume:CSTools:CARL:CSServices

This service helps manage the consumption of streamed chat completion responses, making it easier to implement the consumer side of the producer-consumer pattern in your applications.

Sequence Diagram

Here's a sequence diagram illustrating the producer-consumer process, including the default "carl" service:

sequenceDiagram
    participant U as UI
    participant S as Script
    participant A as AI Service
    participant C as Memcache

    U->>S: Initiate chat (msgID)
    S->>A: streamChatCompletion request
    activate A
    loop For each chunk
        A-->>S: Stream chunk
        S->>C: Store chunk (msgID_i)
        S->>S: Log chunk
    end
    deactivate A
    S-->>U: Completion notification

    loop Until all chunks received
        U->>C: Request chunk (msgID_i)
        C-->>U: Return chunk
        U->>U: Update display
    end
This diagram illustrates the following sequence:

  1. The UI initiates the chat, providing a unique msgID.
  2. The script sends a streaming chat completion request to the AI service.
  3. As the AI service generates the response:
  4. It streams chunks of the response back to the script.
  5. The script stores each chunk in the memcache with a unique key (msgID_i, where i is an incrementing counter).
  6. The script also logs each chunk for debugging purposes.
  7. Once all chunks are received, the script notifies the UI that the completion is finished.
  8. The UI then repeatedly requests chunks from the memcache using the msgID and incrementing counter.
  9. As the UI receives each chunk, it updates the display, creating a real-time streaming effect.

This pattern allows for efficient handling of large responses and provides a smooth, responsive user experience. The memcache serves as a buffer between the AI service's output rate and the UI's consumption rate, ensuring that no data is lost and that the UI can process the response at its own pace.

Adapt it as needed

The actual implementation may vary depending on your specific UI needs.This diagram represents a general approach that can be adapted to various technical environments.

Benefits of Using the Default "carl" Service

When implementing streaming chat completion in your applications, consider using the provided "carl" service to streamline your development process and ensure robust handling of streamed responses.

Using the provided "carl" service offers several advantages:

  1. Simplified Implementation: You don't need to write your own consumer logic, reducing development time and potential errors.
  2. Consistency: The service ensures a consistent approach to consuming streamed responses across your applications.
  3. Optimization: The service may include optimizations for efficient retrieval and assembly of chunked responses.
  4. Maintenance: As part of Module Suite, the service will be maintained and updated, ensuring compatibility with future versions.

To use the "carl" service in your applications, you can call it from your UI code after initiating a streaming chat completion. The service will handle the retrieval and assembly of the chunked response, allowing you to focus on displaying the results to the user.

Function Calling

Function calling allows the AI to interact directly with Content Server, performing actions or retrieving information as needed. This powerful feature enables the AI to manipulate content and execute operations within the Extended ECM environment.

Example: Creating Folders Using AI

In this example, we'll demonstrate how to use function calling to create folders in Content Server based on natural language input.

def systemPreamble = """You are C.A.R.L. (Content Server Artificial intelligence Resource and Liaison), an LLM designed to help users working with OpenText Extended ECM"""
def defaultTemperature = 0.7
def model = "gpt-4"
def maxTokens = 2000
def msgID = "MyMessage"

try {
    def reqBuilder = openai.newChatCompletionRequestBuilder()
        .model(model)
        .temperature(defaultTemperature)
        .n(1)

    def req = reqBuilder.build()
    req.addChatMessage("system", systemPreamble)
    req.addChatMessage("user", "Create a folder for each month of the year in ${self.parent.ID}")

    // Define the function for creating a folder
    def func = openai.newChatFunctionBuilder()
        .name("createFolder")
        .description("Create a folder in the given space (identified by its ID)")
        .build()

    func.addStringParameter("folderName", "The name of the folder", true)
    func.addNumberParameter("parentID", "Parent Space Identifier", true)

    // Implement the function executor
    func.executor = { jsonArguments ->
        try {    
            def slurper = new JsonSlurper()
            def args = slurper.parseText(jsonArguments)
            def newNode = docman.createFolder(docman.getNode(args.parentID as Long), args.folderName)
            return "Created <a data-ampw='am-action' data-action='am_goTo' data-params='${newNode.ID}' data-toggle='click' href='#'>${newNode.name}</a>"
        } catch(e) {
            log.error("Unable to handle the request", e)
            return "Something went wrong"
        }
    }

    req.setFunctions([func])

    result = openai.createChatCompletion(req)
    out << result.choices[0].message
} catch (Exception e) {
    log.error("An error occurred ", e)
    out << "An error occurred: " + e.getMessage()
}
def systemPreamble = """You are C.A.R.L. (Content Server Artificial intelligence Resource and Liaison), an LLM designed to help users working with OpenText Extended ECM"""
def defaultTemperature = 0.7
def model = "gpt-4"
def maxTokens = 2000
def msgID = "MyMessage"

try {
    def reqBuilder = openai.newChatCompletionRequestBuilder()
        .model(model)
        .temperature(defaultTemperature)
        .n(1)

    def req = reqBuilder.build()
    req.addChatMessage("system", systemPreamble)
    req.addChatMessage("user", "Create a folder for each month of the year in ${self.parent.ID}")

    // Define the function for creating a folder
    def func = openai.newChatFunctionBuilder()
        .name("createFolder")
        .description("Create a folder in the given space (identified by its ID)")
        .build()

    func.addStringParameter("folderName", "The name of the folder", true)
    func.addNumberParameter("parentID", "Parent Space Identifier", true)

    // Implement the function executor
    func.executor = { jsonArguments ->
        try {    
            def slurper = new JsonSlurper()
            def args = slurper.parseText(jsonArguments)
            def newNode = docman.createFolder(docman.getNode(args.parentID as Long), args.folderName)
            return "Created <a data-ampw='am-action' data-action='am_goTo' data-params='${newNode.ID}' data-toggle='click' href='#'>${newNode.name}</a>"
        } catch(e) {
            log.error("Unable to handle the request", e)
            return "Something went wrong"
        }
    }

    req.setFunctions([func])

    result = openai.streamChatCompletion(req, { block->  //This is an asyncrnous method, meaning that the script execution
                                                        // won't wait for the completion to be terminated. The method accept 
                                                        // as its second parameter a closure that will be invoked with the streaming
                                                        // chunks as they become available

        def map = [
            content:block.choices?[0]?.message?.content,
            role:   block.choices?[0]?.message?.role,
            finishReason:(block.choices?[0]?.message?.content != null)?block.choices?[0]?.finishReason:'stop'
        ]
        log.debug("GOT {}", map) // Let's also print it into the log file for debugging
        cache.put(msgID+"_"+iterator++, 500, map) 

    })
    out << result*.content
} catch (Exception e) {
    log.error("An error occurred ", e)
    out << "An error occurred: " + e.getMessage()
}

example 2

This example demonstrates several key concepts:

  • Function Definition: We define a createFolder function using the newChatFunctionBuilder(). This function takes two parameters: folderName and parentID.

  • Function Implementation: The executor closure contains the actual implementation of the function. It uses the Content Server API (docman) to create a new folder.

  • AI Integration: The function is added to the chat completion request, allowing the AI to call it when necessary.

  • Natural Language Processing: The user's request to "Create a folder for each month of the year" is interpreted by the AI, which then calls the createFolder function multiple times to fulfill the request.

  • Error Handling: The implementation includes error handling to manage potential issues during folder creation.

  • Interactive Response: The function returns an HTML link that allows users to navigate directly to the newly created folder.

Benefits of Function Calling

Function calling in Module Suite offers several advantages:

  1. Direct Interaction: The AI can perform actions directly in Content Server, bridging the gap between natural language requests and system operations.

  2. Flexibility: You can define custom functions to extend the AI's capabilities, tailoring it to your specific ECM needs.

  3. Safety: By defining specific functions, you control what actions the AI can perform, ensuring security and preventing unintended operations.

  4. Complex Operations: You can implement complex workflows by combining multiple function calls based on user requests.

Error handling

When implementing functions, ensure proper error handling and logging to maintain system stability and aid in troubleshooting.

Functions everywhere

Consider implementing additional functions for common ECM tasks, such as searching for documents, updating metadata, or initiating workflows. This can greatly enhance the AI's utility within your Extended ECM environment.

Document Assembly

Document assembly is a powerful use case that combines AI-generated content with document creation and manipulation within the Extended ECM system. This approach allows for the automatic generation of documents based on user requests, leveraging AI to create content and Module Suite's capabilities to assemble and store the document.

Example: Create a presentation letter in Word

def systemPreamble = "You are D.O.C.S. (Document Organizing and Creation System) an AI agent tasked to support users in creating documents.The content of the document MUST always be passed to the 'createDocument' function as valid X-HTML code (example: <b>DEMO</b>). Always wrap the content in a single 'div' element. Avoid the usage of HTML tag such as (BR). The font family for every text must be: font-family: Poppins, sans-serif tables must have a borders: 1px solid #fff;"

data = [:]
data.company = [name:"CreativeAnswer SA", address:"Via Penate 4, 6850 Mendrisio Switzerland", description:"""
At CreativeAnswer, we are a united marketing agency & software house blending creative brilliance with tech mastery.

We have developed a unique human-AI approach to help you create standout, cost-effective AI-driven marketing solutions with a competitive edge.

This isn't your typical AI tool. We don't settle for generic AI apps, nor do we just churn out text or images. Our creative and tech teams work in perfect sync, shaping ideas with proprietary AI, adapting and customizing them to your needs, and delivering the personal experience your customers want… effortlessly.

We bring you the most valuable approach to GenAI marketing, tailored exclusively for you. Just give it a try.

CreativeAnswer is a proud part of:
- AnswerModules Group, the award-winning Swiss tech company delivering personalized ECM software to 120+ enterprises, including 10 Global Fortune's 500
- Microsoft for Startups Founders Hub
"""]

data.user    = [name:"Patrick Vitali", role:"CTO"]

def defaultTemperature = 0.7
def model = "gpt-4o"
def maxTokens = 2000
def msgID = "MyMessage"
try{

    def reqBuilder  = openai.newChatCompletionRequestBuilder()
    .model(model)
    .temperature(defaultTemperature)
    .n(1) 

    def req = reqBuilder.build()

    func =  llm.newOpenAIChatFunctionBuilder().name("createDocument")
            .description("Given a title, creates a new document, matching user's requests with content generated by the AI agent").build()

    func.parameters = []
    func.addStringParameter("title",   "The document title", true)
    func.addStringParameter("content", "The document's content. The content must be a valid  X-HTML code (example: <b>DEMO</b>)", true)

    func.executor = { jsonArguments ->
        try{
            def slurper = new JsonSlurper()
            def args = slurper.parseText(jsonArguments)

            if(!args.title.endsWith('.docx')){
                args.title = "${args.title}.docx"
            }

            docTemplate = docman.getNodeByPath("CreativeAnswer:Marketing:Corporate Identity:Template.docx")
            newNode = docman.getNodeByName(docTemplate.parent, args.title)

            //Load contents from a Docx file for processing
            def doc = docx.loadWordDoc(docTemplate)

            //Creates a temporary resouce
            def res = docman.getTempResource("out", "docx") 

            //Updates the custom-xml databinding based on the OpenDoPE standard. Since: 2.3.0
            //Notice the combined used of multiple Content Script services: docx, docman, html, cache
            def xml = ""
            if(newNode){
                //Update xml with custom values
                xml = """<doc>
                            <content>&lt;div&gt;${html.escapeXML(html.htmlToXhtml(args.content))}&lt;/div&gt;</content>
                            <docID>${newNode.ID}</docID>
                        </doc>"""
                doc.updateOpenDoPEBindings(xml, true, true)
                doc.save(newNode)
            }else{
                newNode = doc.save(docTemplate.parent, args.title)
                xml = """<doc>
                            <content>&lt;div&gt;${html.escapeXML(html.htmlToXhtml(args.content))}&lt;/div&gt;</content>
                            <docID>${newNode.ID}</docID>
                        </doc>"""
                doc.updateOpenDoPEBindings(xml, true, true)
                doc.save(newNode)
            }
            ulrEditTemplate = "${url}?func=Edit.Edit&nodeid=${newNode.ID}&uiType=1&viewType=1&nexturl=${params.nextUrl}" 

            return "The requested document has been created with title: ${args.title}. <br>You can finalize the draft using the link that follows:<a target='_blank' href='${ulrEditTemplate}'>Edit</a>"

        }catch(e){
            log.error("Error ",e)
            return "Something went wrong "+e.message
        }
    }

    req.setFunctions([func])

    req.addChatMessage("system", //one out: system, assistant, user
                    systemPreamble) //We instruct the agent about its purpose and constraints using system messages at the beginning
    //of the chat
    req.addChatMessage("system", "When creating documents you should consider the following context, expressed in the form of a JSON structure: ${JsonOutput.toJson(data)}")

    //The user request   
    req.addChatMessage("user", "Create a presentation letter to be sent to John Doe, ACME's CMO") //Add user request or message

    result = openai.streamChatCompletion(req, { block->
        def map = [
            content:block.choices?[0]?.message?.content,
            role:   block.choices?[0]?.message?.role,
            finishReason:(block.choices?[0]?.message?.content != null)?block.choices?[0]?.finishReason:'stop'
        ]
        log.debug("GOT {}", map) // Let's also print it into the log file for debugging
        cache.put(msgID+"_"+iterator++, 500, map) 

    })
    out << result*.content

} catch (Exception e) {
    out << "An error occurred: " + e.getMessage()
}

example 2

This example demonstrates how to create a presentation letter using AI-generated content and Module Suite's document manipulation features.

Key components of this implementation include:

  1. AI Content Generation: The AI is instructed to create document content based on user requests and provided context.

  2. Document Creation Function: A specialized function is made available to the AI for creating documents within the ECM system.

  3. Content Formatting: The AI is guided to provide content in a specific format (X-HTML) to ensure compatibility with the document creation process.

  4. Context Provision: Relevant data, such as company and user information, is provided to the AI to inform the content generation process.

  5. Document Template Usage: The implementation utilizes a predefined document template as a base for new documents.

  6. Dynamic Document Update: The system checks for existing documents with the given title, updating if found or creating new ones if not.

  7. OpenDoPE Standard: The implementation leverages the OpenDoPE standard for XML data binding, allowing for dynamic content insertion into documents.

  8. Streaming Response: The AI's response is streamed, enabling real-time updates and potentially faster response times for large documents.

Benefits of AI-Assisted Document Assembly

  1. Efficiency: Automates the process of creating standard documents, saving time and reducing manual effort.

  2. Consistency: Ensures that created documents follow a consistent structure and style.

  3. Contextual Relevance: By providing context to the AI, the generated content can be highly relevant and personalized.

  4. Flexibility: Can be adapted for various document types and use cases within the ECM system.

  5. Integration: Seamlessly combines AI capabilities with existing ECM features and document templates.

Additional considerations: Implementation details

When implementing AI-assisted document assembly:

  • Ensure that the AI is properly constrained to generate content in the required format and style.
  • Regularly validate the generated content to maintain quality and consistency.
  • Consider implementing error handling and logging to manage potential issues during document creation.
  • Design the user interface to provide clear feedback on the document creation process and results.

Note

The specific implementation details may vary depending on your ECM environment and chosen AI service. Consult your Module Suite documentation for precise integration steps.

Tip

Consider expanding this approach to other document types, such as reports, contracts, or proposals. You can create specialized templates and AI instructions for each document type to further streamline your document creation processes.

Embedding Index Generation

Embedding index generation is a crucial step in creating AI-powered search and retrieval systems within your ECM environment. This use case demonstrates how Module Suite can generate embedding indexes for documents, enabling advanced semantic search capabilities.

Example: Indexing a single document

The provided implementation showcases the generation of an embedding index for documents in the ECM system. A key feature of this approach is its flexibility in handling different document types and chunking methods.

    try {
        // Retrieve the document node from the Enterprise Workspace
        def document = docman.getNodeByPath(docman.getEnterpriseWS(), "Documents:MS_3_7_0_Manual.pdf")
        def docID = document.ID as String
        def lastVersion = document.lastVersion
        def list = null
        def sources = [:]

        // Prepare document metadata for indexing
        // We store metadata instead of raw text for efficiency:
        // 1. Document ID
        // 2. Document type (pdf or raw)
        // 3. Chunk dimension (e.g., page number or word count)
        // 4. Chunk identifier
        if (lastVersion.mimeType == "application/pdf") {
            // For PDF documents: Extract text page by page
            def content = pdf.getTextForPages(document)
            sources = content.collectEntries { entry -> 
                [("${docID}_pdf_1_${entry.key}"): entry.value] 
            }
        } else {
            // For non-PDF documents: Split content into word-based chunks
            def content = docman.getContentAsRawText(document)
            def chunkSize = 100 // Number of words per chunk (adjustable)
            def index = 0
            sources = content.split("\\s").toList().collate(chunkSize) *.join(" ").collectEntries {
                val -> [("${docID}_raw_${chunkSize}_${index++}"): val]
            }
        }

        // Process sources in batches of 10
        list = sources.entrySet().toList().findAll { it.value }.collate(10)
        def chunk = 0

        list.each { subEnties ->
            log.debug("Processing Chunk ${chunk} {}", subEnties *.value)
            try {
                // Generate embeddings for the current batch
                def req = llm.newOpenAIEmbeddingRequestBuilder()
                            .model("text-embedding-ada-002")
                            .input(subEnties *.value)
                            .build()
                def embeddings = llm.createEmbeddings(req).data

                // Build vectorial index using embeddings and metadata
                llm.newCSVectorialIndexManager().buildIndex(docID, embeddings, subEnties *.key)
            } catch (e) {
                // Log errors for individual chunks without stopping the entire process
                log.error("Unable to process chunk ${chunk}", e)
            }
            log.debug("End processing Chunk ${chunk++}")
        }
    } catch (e) {
        // Log any overall process errors
        log.error("An error occurred during index generation.", e)
    }
try {
    // Retrieve the document node using the provided path
    def document = docman.getNode(2031317 )
    def docID = document.ID as String
    def lastVersion = document.lastVersion
    def list = null
    def sources = [:]

    if (lastVersion.mimeType == "application/pdf") {
        // If the document is a PDF, extract text for each page and create embeddings
        def content = pdf.getTextForPages(document)
        sources = content.collectEntries { entry -> [("${docID}_pdf_1_${entry.key}"): entry.value] }
    } else {
        // If not a PDF, split the content into chunks and generate embeddings
        def content = docman.getContentAsRawText(document)
        def chunkSize = 100 // The number of words each chunk should be made of
        def index = 0
        sources = content.split("\\s").toList().collate(chunkSize) *.join(" ").collectEntries {
            val -> [("${docID}_raw_${chunkSize}_${index++}"): val]
        }

    }

    list = sources.entrySet().toList().findAll { it.value }.collate(10)
    def chunk = 0
    list.each { subEnties ->
        log.debug("Processing Chunk ${chunk} {}", subEnties *.value)
        try {
            // Create an embedding request for the chunk

            model = llm.newCSEmbeddingModelBuilder("ollama")
            .modelName("llama3")
            .logRequests(true)
            .logResponses(true)
            .build();

            def embeddings = []
            subEnties.each{
                embeddings << llm.newCSEmbedding( model.embed(it.value) )
            }

            // Build a vectorial index for the document using the embeddings
            llm.newCSVectorialIndexManager().buildIndex(docID, embeddings, subEnties*.key)
        } catch (e) {
            log.error("Unable to process chunk ${chunk}", e)
        }
        log.debug("End processing Chunk ${chunk++}")
    }
} catch (e) {
    log.error("An error occurred. ", e)
}

Key Features

  1. Document Type Flexibility: The system can handle various document types, with specific handling for PDF files and a general approach for other text-based documents.

  2. Adaptive Chunking: The chunking strategy adapts based on the document type:

  3. For PDFs: Text is extracted page by page.
  4. For other documents: Content is split into chunks based on a specified word count.

  5. Metadata-Rich Indexing: Instead of storing raw text, the index stores metadata that can be used to retrieve the original text:

  6. Document ID
  7. Document type (PDF or raw)
  8. Chunk dimensions (e.g., page number for PDFs, word count for other documents)
  9. Chunk identifier

  10. Configurable Chunk Size: For non-PDF documents, the chunk size (number of words per chunk) can be easily adjusted.

  11. Batch Processing: Embeddings are generated and indexed in batches to manage resource usage efficiently.

Additional Considerations: Implementation Highlights

This implementation demonstrates several important concepts:

  1. Document Retrieval: The system retrieves documents from the ECM using the document management API.
  2. Type-Specific Processing: Different processing logic is applied based on the document's MIME type.
  3. Flexible Text Extraction:

    • For PDFs, text is extracted page by page using a PDF processing service.
    • For other documents, raw text is extracted and split into word-based chunks.
  4. Metadata Generation: Each chunk is associated with a unique identifier that includes:

    • Document ID
    • Document type (PDF or raw)
    • Chunk size or page number
    • Chunk index
  5. Embedding Generation: The system uses an LLM service to generate embeddings for each chunk of text.

  6. Index Building: A vectorial index is built using the generated embeddings and associated metadata.

Retrieval-Augmented Generation (RAG)

Retrieval-Augmented Generation (RAG) is a powerful technique that combines the strengths of large language models with the ability to access and utilize specific, up-to-date information from your Extended ECM system. This approach significantly enhances the accuracy and relevance of AI-generated responses by grounding them in your organization's actual content.

Example: Using a all the documents in a folder as a Knowledge Base

In this use case, we demonstrate how to implement a RAG system within the Module Suite. The system performs the following key steps:

  1. Embeds the user's question using a specified embedding model.
  2. Searches a pre-built set of vector indexes of your ECM content for relevant information.
  3. Retrieves the actual text content of the most relevant chunks.
  4. Incorporates this retrieved context into the prompt for the large language model.
  5. Generates a response using the LLM, now informed by the relevant context.

An index for each document

This example assumes that an embedding index has been created for each document

// Configuration parameters for the RAG system
def numberOfSnippetToConsider = 1      // Number of relevant snippets to use for context
def distanceSnippetTrashold = 0.8      // Cosine similarity threshold for relevant snippets
def embeddingModel = "text-embedding-ada-002"  // Model used for generating embeddings
def defaultTemperature = 0.8           // Temperature for LLM response generation
def model = "gpt-4"                    // LLM model to use for generating responses
def maxTokens = 2000                   // Maximum number of tokens in the LLM response
def msgID = "msgID"                    // Unique identifier for the message (used in streaming scenarios)

// Function to retrieve relevant context based on the user's question
def getContext = { String prompt ->
    // Get all document IDs in the KB folder (assuming documents are of subtype 144)
    def indexes = docman.getNodeByPath("KB").childrenFast.findAll{it.subtype == 144}.collect{it.ID as String}

    // Generate embedding for the user's prompt
    def req = openai.newEmbeddingRequestBuilder().model(embeddingModel).input([prompt]).build()
    def promptEmbedding = openai.createEmbeddings(req).data

    // Search the vector indexes for similar content
    def context = llm.newCSVectorialIndexManager().getIndexesSearcher(*indexes)
                    .query(promptEmbedding[0], 100, 100)
                    .findAll{it.score >= distanceSnippetTrashold}

    def textContext = ""
    if(context) {
        // Parse the index key to extract document information
        // Format: DOCID_identifier_chunkDimension_chunkNumber
        def (docID, extMode, dimension, chunkNum) = context[0].text.split("_")

        // Retrieve the actual text content based on the extraction mode
        switch(extMode) {
            case "raw":
                // For raw text, split into words and retrieve the specific chunk
                textContext = docman.getContentAsRawText(docman.getNodeFast(docID as Long))
                                .split("\\s")
                                .toList()
                                .collate(dimension as int)
                                *.join(" ")[chunkNum as int]
                break
            case "pdf":
                // For PDFs, retrieve the specific page(s)
                textContext = pdf.getTextForPages(docman.getNodeFast(docID as Long), 
                                                chunkNum as int, 
                                                (chunkNum as int) + (dimension as int))[(chunkNum as int)]
                break
        }
    }
    return textContext
}

// Main execution
def question = "Which is the biggest update on Module Suite 3.2.0 ?"

// Set up the chat completion request
def builder = openai.newChatCompletionRequestBuilder()
def req = builder.model(model)
                .user("u" + users.current.ID)
                .temperature(defaultTemperature)
                .n(1)
                .build()

// Add system message to define CARL's role
req.addChatMessage("system", "You're C.A.R.L. (Content Server Artificial intelligence Resource and Liaison) an LLM designed to help users working with OpenText Extended ECM")

// Retrieve relevant context for the question
def context = getContext(question)

// Add user message with or without context
if(context) {
    req.addChatMessage("user", """Given the following context: ${context}
Answer the user question: ${question}""")
} else {
    req.addChatMessage("user", question)
}

// Generate and output the response
result = openai.createChatCompletion(req)
out << result.choices[0].message.content

Key Components

  1. Embedding Generation: The system uses the OpenAI API to generate embeddings for the user's question.

  2. Vector Index Search: A custom CSVectorialIndexManager is used to search pre-built indexes of your ECM content.

  3. Context Retrieval: Based on the search results, the system retrieves the actual text content from your ECM documents, handling different document types (e.g., raw text, PDF) appropriately.

  4. LLM Integration: The retrieved context is incorporated into the prompt sent to the LLM (in this case, GPT-4), allowing it to generate more accurate and relevant responses.

Benefits

  1. Improved Accuracy: By grounding the LLM's responses in your actual ECM content, the system provides more accurate and relevant answers.

  2. Up-to-date Information: The system can access the latest information in your ECM, ensuring responses reflect the most current data.

  3. Customization: The RAG approach allows the AI to leverage your organization's specific knowledge and terminology.

  4. Reduced Hallucination: By providing relevant context, the likelihood of the LLM generating incorrect or fabricated information is significantly reduced.

Additional Considerations: Implementation Highlights

  • Index Management: Ensure that your vector indexes are kept up-to-date as your ECM content changes.

  • Performance Optimization: Consider caching frequently accessed content or embeddings to improve response times.

  • Privacy and Security: Be mindful of what content is being sent to external LLM services and ensure compliance with your organization's data policies.

This implementation demonstrates a basic RAG system that can be further customized and optimized based on your specific needs and use cases within the Extended ECM environment.

Close the loop

Consider implementing a feedback mechanism to continuously improve the relevance of retrieved contexts and the quality of generated responses.

Do not index just once

The effectiveness of the RAG system heavily depends on the quality and coverage of your vector indexes. Regular maintenance and updates of these indexes are crucial for optimal performance.

Configuration

Base Configuration parameters description, default and purpose.

CARL Service Configuration Overview

The following table outlines the configuration parameters for the CARL service:

Parameter Default Value Description
amcs.carl.enabled false Enable or disable CARL feature
amcs.carl.default_mode chat Default mode for CARL (deprecated, chat is the only available mode)
amcs.carl.kb_cs_override empty Override for CARL co-pilot's knowledge base (KB)
amcs.carl.default_temperature 0.5D CARL's request default temperature
amcs.carl.kb_cs_context 2 Number of CARL's KB entries to consider as context for each request
amcs.carl.kb_cs_distance 0.75D Threshold for cosine distance among CARL's KB entries
amcs.carl.default_lang EN CARL's default language
amcs.carl.default_emb_model text-embedding-ada-002 Default embedding model (used for updating the KB)
amcs.carl.kb_context_maxlen 20000 Maximum length of KB entries to be included in the request (in characters)
amcs.carl.default_maxtokens 8000 Default number of tokens to be used in completion requests
amcs.carl.auth_id empty Reserved for future use
amcs.carl.auth_secret empty The OpenAI API key for your organization
amcs.carl.auth_uri empty Reserved for future use
amcs.carl.api_uri https://api.openai.com/v1/ OpenAI API endpoint
amcs.carl.api_version empty Reserved for future use

Deprecated Parameter

The amcs.carl.default_mode parameter is considered deprecated. It exists for historical reasons when LLMs first became popular and supported text completion. Currently, almost everything is done in the form of chat completion.

Knowledge Base Override

The amcs.carl.kb_cs_override parameter is optional and should only be used if you want to override the knowledge base of the CARL co-pilot. The KB of CARL (the Content Script co-pilot) has been generated based on the content of the Content Script Volume, specifically the Content Script Snippets.

The carl service features an API carl.dumpCarlKB() that allows you to regenerate this KB so that you can save it as a document on Content Server. Under The Content Script Volume:CSTools:CARL:Utilities, there is a script named "ExportCARLKB" that implements this concept. It should be used in case you have custom snippets you want to be included in CARL's KB.

Cosine Distance Threshold

The amcs.carl.kb_cs_distance parameter sets the threshold for the cosine distance among CARL's KB entries. This value determines whether an entry is considered relevant and should be included in building a request context. A lower value will result in more strict matching, while a higher value will be more lenient.

Token Limit

The amcs.carl.default_maxtokens parameter sets the default number of tokens to be used in completion requests. Be aware that different AI models may have different maximum token limits. Ensure this value does not exceed the limit of your chosen model.

API Key Security

The amcs.carl.auth_secret parameter is used to store your OpenAI API key. Ensure that this value is kept secure and not exposed in any logs or public configurations. It's recommended to use secure methods for storing and managing API keys in your production environment.

API Endpoint

The amcs.carl.api_uri parameter is set to the default OpenAI API endpoint. If you're using a different provider or a custom endpoint, you'll need to update this value accordingly.

LLM Service Configuration Overview

The following table outlines the configuration parameters for the LLM service:

Parameter Default Value Description
amcs.llm.activeProfiles default Comma-separated list of active profiles
amcs.llm.provider.default openai The LLM API Service provider
amcs.llm.auth_id.default empty Used if the provider uses OAuth authentication
amcs.llm.auth_secret.default empty The provider's integration key (secret)
amcs.llm.auth_uri.default empty The URL for generating an authorization URL (for OAuth)
amcs.llm.api_uri.default https://api.openai.com/v1/ The APIs endpoint
amcs.llm.model_id.default empty The default model to be used (deprecated)
amcs.llm.temperature.default 0.0 The default request's temperature (deprecated)
amcs.llm.net_timeout.default 3000 The default network timeout (in milliseconds)
amcs.llm.net_log_enabled.default false Network Request and Responses Logging
amcs.llm.index_store empty The storage path for embedding indexes

Active Profiles

The amcs.llm.activeProfiles parameter supports multiple profile configurations. You can register a new profile by adding the proper "Custom variables" to the Base Configuration. This property allows you to enable existing profiles. Profiles that are not enabled are not considered.

Supported Providers

As of the time of writing, the supported LLM API Service providers (amcs.llm.provider.default) are: - openai - azure - ollama

Deprecated Parameters

The following parameters are marked as deprecated: - amcs.llm.model_id.default: The default model to be used - amcs.llm.temperature.default: The default request's temperature

Consider using alternative methods to specify these values in your requests.

API Key Security

The amcs.llm.auth_secret.default parameter is used to store your LLM provider's API key. Ensure that this value is kept secure and not exposed in any logs or public configurations. It's recommended to use secure methods for storing and managing API keys in your production environment.

Network Logging

The amcs.llm.net_log_enabled.default parameter enables logging of network requests and responses. This should be set to false in production environments to prevent potential exposure of sensitive information.

Embedding Index Storage

The amcs.llm.index_store parameter specifies the storage path for embedding indexes. In most cases, this should be a shared storage location accessible to all cluster nodes (e.g., EFS) to ensure consistency across the system (e.g. \\otadminBe01\EFS\llm_indexes\).

OAuth Authentication

If your LLM provider uses OAuth authentication, you'll need to set the amcs.llm.auth_id.default and amcs.llm.auth_uri.default parameters accordingly. These are used for generating and managing OAuth tokens.

Defining New LLM Service Profiles

To create a new profile for the LLM service, you need to add custom variables to the Base Configuration. The process involves replicating the existing configuration parameters but replacing the "default" suffix with the name of your new profile. This allows you to maintain multiple configurations for different LLM providers or use cases within the same system.

For example, to create an "azure" profile for Azure AI, you would add the following custom variables:

Parameter Value
amcs.llm.api_uri.azure https://amai-east-us.openai.azure.com/
amcs.llm.auth_secret.azure 7a234aaaa666666d5a245245422cvbs23a
amcs.llm.provider.azure azure

Similarly, to create an "ollama_prod" profile for Ollama, you would add:

Parameter Value
amcs.llm.provider.ollama_prod ollama
amcs.llm.api_uri.ollama_prod http://15.201.113.243:11434

By defining these custom variables, you create distinct profiles that can be activated using the amcs.llm.activeProfiles parameter. This flexibility allows you to easily switch between different LLM providers or configurations without modifying the core settings. Remember to include all necessary parameters for each profile to ensure proper functionality.

Profile Naming

Choose clear and descriptive names for your profiles to easily identify their purpose or associated provider. For instance, use names like "azure_prod", "openai_dev", or "ollama_test" to indicate both the provider and the environment.

Secure Configuration

When defining profiles, especially those containing sensitive information like API keys, ensure that your configuration files and storage are properly secured and follow your organization's security best practices.