Member Junction
    Preparing search index...

    Module @memberjunction/ai-betty - v6.2.0-edge.5

    @memberjunction/ai-betty

    MemberJunction provider for Betty, the MJ-native organization-scoped assistant. Implements BaseLLM against the Betty Public API v1, so an MJ prompt or agent can call Betty like any other model.

    They are different services, not two versions of one. This package exists alongside the legacy one — nothing in ai-betty-bot changes, no deployment has to migrate, and both can run in the same instance.

    BettyBotLLM (legacy) BettyLLM (this package)
    Host betty-api.tasio.co per-customer Betty deployment
    Auth key → POST /settings → JWT → bearer JWT key is the bearer
    Endpoint POST /response POST /messages
    Request { input } { message, conversationId?, context? }
    Response { response, references[{link,title,type}] } { conversationId, messageId, response, references[{title,url?,contentItemId?}], requestId }
    History first user message only latest user turn + prior turns as context
    Env var AI_VENDOR_API_KEY__BETTYBOTLLM AI_VENDOR_API_KEY__BETTYLLM
    Base URL BETTY_BOT_BASE_URL (defaulted) BETTY_API_BASE_URL (required)

    Migrating a deployment is repointing one AIModelVendor.DriverClass from BettyBotLLM to BettyLLM; rolling back is repointing it back.

    BETTY_API_BASE_URL=https://<host>/betty/v1      # required — include the version segment
    AI_VENDOR_API_KEY__BETTYLLM=<betty api key> # from Insights › External › API keys

    BETTY_API_BASE_URL has no default on purpose. Betty is deployed per customer, so a default would let a misconfigured instance answer from another tenant. Unset, the first call fails with a message naming the variable.

    The API key needs the betty:chat scope. Credentials can also come through MJ's credential system (AICredentialBinding), which outranks the environment variable.

    Runtime resolution is AIModelVendor.DriverClass → this class. You need:

    1. MJ: AI Vendors — a vendor row
    2. MJ: AI Models — a model row, AIModelTypeID = LLM
    3. MJ: AI Model Vendors — two rows: a Model Developer row, and an Inference Provider row carrying DriverClass = 'BettyLLM' and an APIName

    Do not create MJ: AI Vendor Type Definitions rows — they are migration-seeded; reference them by @lookup.

    The constructor takes an optional second argument, forwarded as Betty's endUserId request field:

    new BettyLLM(apiKey, 'izzy')
    

    Betty records it against the conversation, so the caller gets its own attribution row and its own rate-limit bucket instead of sharing an anonymous per-IP one. Without it, utilization reports read a server as a single anonymous client — the problem PR #4113 fixes for BettyBotLLM with its userId argument; this is the same idea under the field name Betty's own API documents.

    Attribution only, and unverified by design: retrieval scope is pinned server-side from the credential, so it never widens what content a request can reach. Do not use it for authorization.

    Omit it and the request body is byte-identical to one built without the parameter.

    Note it is reachable only by constructing the provider directly. AIPromptRunner instantiates via ClassFactory.CreateInstance(BaseLLM, driverClass, apiKey) and passes the key alone, and ChatParams carries no user identity to fall back on — so a prompt run through the normal model path is anonymous, exactly as the legacy provider is.

    Betty threads conversations server-side through conversationId, but ChatParams carries no correlation id and a fresh provider instance is constructed per execution, so there is nothing to thread it through. MJ owns the history instead: the latest user message is the turn, and everything before it goes in context.text — which the API documents as advisory and explicitly non-authorizing, so it informs the answer without widening what the credential can reach.

    This is the one behaviour deliberately not carried over from the legacy provider, which uses messages.find(m => m.role === user) — .find returns the first match, so a three-turn exchange sends the opening question and discards the follow-up.

    References ride as extra choices, matching BettyBotLLM exactly:

    • choices[0] — the answer
    • choices[1] — references formatted for display
    • choices[2] — raw JSON, finish_reason: 'references_json'

    Kept identical so anything already parsing the legacy provider's output keeps working across a DriverClass repoint.

    • No streaming. SupportsStreaming returns false. The API does support SSE via Accept: text/event-stream, so this is a clean follow-up rather than a dead end.
    • SummarizeText and ClassifyText are not implemented.
    • Token usage is reported as zero — the endpoint does not return counts.

    Classes

    BettyLLM

    Type Aliases

    BettyChatRequest
    BettyChatResponse
    BettyReference
    BettyRequestContext

    Variables

    BETTY_BASE_URL_VAR

    Functions

    BettyEndpoint
    GetBettyBaseURL