@callimacus/thamyr

中文文档
4.23.5发布于 2026/11/09SEE LICENSE IN LICENSE.mdTypeScriptES Module近 30 天下载 6486

JavaScript package for Thamyr

原始使用文档
文档目录39 节
  1. Overview
  2. Core Concepts
  3. 1. Story
  4. 2. Chapter
  5. 3. InputEvent
  6. 4. Response
  7. Blocks
  8. How Thamyr Works
  9. Technical Setup
  10. Prerequisites
  11. Cloning the Repository
  12. Installing the Correct Node Version
  13. Branching and Development Workflow
  14. Credentials Management
  15. Code Quality Checks
  16. Creating a Pull Request (PR)
  17. Deployment
  18. Troubleshooting
  19. How to Use the Thamyr SDK
  20. Setup and Initialization
  21. Sending User Input
  22. Rounds Without a Message: Landing, Interactions, Refresh
  23. Suggestions for the Input Box
  24. Reading a Round
  25. Block Patches
  26. Rendering Blocks
  27. Full Example of Block Rendering in Action
  28. Example of a Block Component (ImageBlock)
  29. API Namespaces
  30. Available Namespaces
  31. Working with Results
  32. Content API
  33. Stories API
  34. Products API
  35. Whisper API
  36. Analytics API
  37. Cart API
  38. Skesis and Sitemap APIs
  39. Upgrading from 1.x, 2.x or 3.x

Thamyr SDK Documentation

Overview

Thamyr is the AI agent within Callimacus that is designed to generate and arrange dynamic, intuitive user interfaces that adapt to user intent and context. By leveraging proprietary AI algorithms, Thamyr transcends traditional typographic approaches to website design, creating a more immersive and context-sensitive experience for users.

Core Concepts

1. Story

A Story is the overarching structure that encapsulates a user's journey, intent, and context. It is made up of multiple Chapters, each representing a distinct part of the interaction. Stories ensure continuity by preserving the user's intent and context, allowing Thamyr to maintain relevance across different interactions.

2. Chapter

A Chapter is a component of a Story that responds to specific user actions or inputs. Each chapter is generated in response to an inputEvent and can evolve based on prior interactions.

3. InputEvent

An inputEvent is any user interaction that triggers Thamyr to respond. Input events can be:

  • A click on an element of the website.
  • A user query (written or audio).

4. Response

Thamyr processes input events to produce a response, which is made up of different types of blocks. These blocks are the building blocks of the UI and represent different forms of content.

Blocks

A Block is a unit of content generated by Thamyr in response to a user input. Each block can be one of the following types:

  • Text Block: A simple textual response or message.
  • Image: A visual representation, such as a single image.
  • Gallery: A collection of images displayed together.
  • Video: A media block containing a video.
  • Rich Media: A block that can contain complex content, such as embedded elements or interactive features.
  • Topic: A block that introduces a new subject or theme related to the context.
  • Custom Element: A user-defined block type that can be customized to meet specific needs or functionalities.

How Thamyr Works

Receiving Input: When an inputEvent occurs (e.g., a user clicks on a button or asks a question), Thamyr receives and processes it. Generating the Story: Based on the input event, Thamyr identifies the appropriate Chapter within the Story and produces a relevant response. Creating Blocks: Thamyr's response consists of one or more blocks, which are then arranged to create the best possible user interface for the given context.


Technical Setup

Prerequisites

Before you begin, ensure that you have the following:

  • GitHub Account: You must have a GitHub account to access and contribute to the project.
  • Git and Node.js: Ensure you have Git and Node.js installed on your local machine.
  • nvm (Node Version Manager): This project uses nvm to manage Node.js versions. Make sure you have nvm installed.

Cloning the Repository

  1. Access the Repository: After being granted access to the repository, you will receive a link to clone it. Use the following command to clone the repository to your local machine:

    git clone https://github.com/your-org/your-repo.git
    
  2. Navigate to the Repository:

    cd your-repo
    

Installing the Correct Node Version

  1. Install Node Version: The repository contains a .nvmrc file that specifies the Node.js version to be used. To install the correct version, run the following command:

    nvm install
    

    This will automatically install the version of Node.js specified in the .nvmrc file.

  2. Use the Correct Version: Once the installation is complete, you can use the specified Node version by running:

    nvm use
    

Branching and Development Workflow

  1. Create a Development Branch: The main branch is used for production, so create a new branch for any feature or bug fix. Branches must follow the naming convention:

    branch.repo.gymnasium.callimacus.ai
    

    Example:

    git checkout -b new-feature.repo.gymnasium.callimacus.ai
    

    This ensures that each branch has a unique URL when deployed.

  2. Make Changes: Develop and test your changes locally. Be sure to follow the project's coding conventions.

  3. Commit Messages: Commit messages must follow the guidelines outlined in the Conventional Commits specification. This helps ensure a consistent commit history and automated versioning.

    Example commit message:

    feat(button): add primary button style
    
  4. Push Changes: After completing your work, commit your changes and push them to GitHub:

    git add .
    git commit -m "feat(new-feature): implement new feature"
    git push origin new-feature.repo.gymnasium.callimacus.ai
    

Credentials Management

  1. Basic HTTP Authentication: All deployed URLs are protected by basic HTTP authentication. You can manage the credentials for these URLs via the Callimacus Backoffice.

  2. Client ID: Callimacus requires a Client ID (cal-pk-…) for authentication to function properly. To use it, initialize the Callimacus instance with your Client ID as shown below:

    const instance = new Thamyr({
        clientId: import.meta.env.VITE_CALLIMACUS_CLIENT_ID,
    });
    

You can manage and retrieve your Client ID via the Callimacus Backoffice. Make sure to securely store the Client ID and use it in the appropriate environment variables (VITE_CALLIMACUS_CLIENT_ID).

Code Quality Checks

Before merging your code into the main branch, ensure that your code passes the quality checks. These checks include:

  • Linting: To check the code style and ensure best practices.
  • Code Coverage: To ensure adequate testing and test coverage for your changes.

The quality checks are automatically run through the CI pipeline when a pull request (PR) is created. However, you can run them locally before pushing your changes:

npm run build && npm run lint && npm run test
  • Linting: This command will check for code style issues.
  • Testing: This command will run tests to verify that your code behaves as expected.

Creating a Pull Request (PR)

  1. Open a PR: Once your code is ready, create a pull request (PR) from your branch (e.g., new-feature.repo.gymnasium.callimacus.ai) to the main branch.

  2. Passing Quality Checks: Ensure that the CI pipeline passes all checks (linting, tests, and coverage) before merging. You can monitor the PR’s status for any errors or failed checks.

  3. Merge to Main: After the PR passes all checks and gets reviewed, you can merge it into the main branch. This will trigger the deployment to production.

Deployment

  • Frontend Hosting: The frontend is automatically hosted by Callimacus. Once merged to the main branch, the latest changes will be deployed to the production environment.
  • Preview Environments: Each branch you create will automatically have a unique preview URL under the format branch.repo.gymnasium.callimacus.ai.

Troubleshooting

  • Quality Check Failures: If your PR fails any quality checks, review the error messages in the CI pipeline. Typically, these errors are related to linting or failing tests. Run npm run lint and npm run test locally to identify and fix issues before pushing again.

  • Branch Naming Issues: Ensure that your branch name follows the correct format. If the branch name does not match the required pattern, it may not be deployed to a preview environment.

  • Merge Conflicts: If you encounter merge conflicts, resolve them by pulling the latest changes from the main branch and updating your feature branch:

    git pull origin main
    

How to Use the Thamyr SDK

Setup and Initialization

To use the Thamyr SDK in your project, follow these steps:

  1. Install the SDK: First, you need to install the Thamyr SDK package. Run the following command in your project directory:

    npm install @callimacus/thamyr
    
  2. Initialize Thamyr: In your React component, initialize the Thamyr instance with your Client ID, which is required for API interaction. The Client ID is typically stored in an environment variable for security purposes.

    Example:

    import { useEffect, useRef, useState } from 'react';
    import { Thamyr, ThamyrResponseType } from '@callimacus/thamyr';
    
    function App() {
        const [chapters, setChapters] = useState([]);
        const thamyrRef = useRef(null);
    
        useEffect(() => {
            const instance = new Thamyr({
                 clientId: import.meta.env.VITE_CALLIMACUS_CLIENT_ID,
            });
    
            thamyrRef.current = instance;
    
            instance.onResponse(response => {
                if (response.type === ThamyrResponseType.CHAPTER_EVENT) {
                    setChapters(prev => {
                        const existingIndex = prev.findIndex(c => c.id === response.chapter.id);
                        if (existingIndex !== -1) {
                            const updated = [...prev];
                            updated[existingIndex] = response.chapter;
                            return updated;
                        } else {
                            return [...prev, response.chapter];
                        }
                    });
                }
            });
        }, []);
    }
    

Tip: Building with React? @callimacus/thamyr-react wraps this SDK in hooks (useInitThamyr({clientId}), useThamyr, useConnection, useOnResponse, useOnConnectionChange, useOnError, useOnWarning, useApi, useCart, useSkesis, useSitemap, useMapPage, useCallimacusStore, plus useVoiceRecorder and useTtsAudio for voice input and text-to-speech playback) so you never manage the instance yourself.

Sending User Input

To trigger interactions and send user input, you'll need to handle user events like text input or clicks. In the example below, we send a question as an input event.

  1. Handle User Input: Collect user input (e.g., from a text box) and send it to Thamyr as an input event.

    import { UserInteractionType, SLInputEventType } from '@callimacus/thamyr';
    
    const handleAsk = () => {
        const inputSL = {
            type: SLInputEventType.question, // Type of input event
            data: { value: inputText } // User's input text
        };
        setInputText("Type your next question");
    
        thamyrRef.current?.sendUserInteraction(UserInteractionType.CREATE_ROUND, {
            inputEvent: inputSL,
        });
    };
    
  2. Triggering User Interaction: When the user presses the Enter key, you send the question to Thamyr.

    const handleKeyDown = (e) => {
        if (e.key === "Enter") {
            e.preventDefault();
            handleAsk();
        }
    };
    

Rounds Without a Message: Landing, Interactions, Refresh

Not every round starts with a question. The tenant's configuration document declares a landing page and a registry of named interactions the storefront may send; the SDK opens those rounds with three helpers, so you never assemble the input event by hand.

// The landing page, composed from the tenant's landing configuration.
// `value` is the landing value its blocks read ({{input.value}}) — a page slug, a campaign; at most 200 characters.
thamyr.openLanding('spring-edit', { language: 'it' });

// A declared interaction, with the block it comes from and what it carries.
thamyr.sendInteraction('pick-size', { block: 'hero', payload: { size: 'M' }, storyId });

// A refresh: the interaction sent with the block's key. The round composes like any other; a block
// whose `patchOn` names the interaction is updated in place instead (see Block patches).
thamyr.refresh('hero', 'reshuffle', { storyId });

Every helper takes storyId and language like CREATE_ROUND does; an interaction the tenant declared as system is recorded and closed with nothing composed.

Type sendInteraction from the tenant's registry by augmenting ThamyrInteractionMap once in your project: the id becomes a union of the declared ids and each payload is checked.

declare module '@callimacus/thamyr-core' {
  interface ThamyrInteractionMap {
    'pick-size': { size: string };
    reshuffle: undefined;
  }
}

In React the same three helpers come from useThamyr() and pass through its connection gate:

const { openLanding, sendInteraction, refresh } = useThamyr();

Suggestions for the Input Box

suggest(prefix) answers through the socket acknowledgment, never as a story response: the tenant's starter questions while the prefix is shorter than three characters, autocomplete from there. It never rejects — a disabled tenant, a lost connection or a timeout (4 s by default) all resolve to no suggestions.

const { suggestions } = await thamyr.suggest(inputText, { storyId, timeoutMs: 2000 });

In React, useSuggestions(prefix, { storyId }) follows the input as the visitor types — debounced (200 ms by default, the empty prefix asks at once), and a late answer never overwrites a newer prefix:

const [text, setText] = useState('');
const { suggestions, loading } = useSuggestions(text, { storyId });

Reading a Round

Three things a round carries are worth reading by name rather than by digging into data:

  • Receptors — the labels the classifier assigned this message (not the session intent). receptorsOf(chapter) reads chapter.receptors, falling back to the intent name older servers used.
  • Block key — the identity a block is attributed, pinned and linked by; block.type is a render hint two blocks may share. blockKeyOf(block) reads it off a custom block (legacy blocks have none).
  • Add-to-cart result — when the flow adds to cart, every block of the round carries how it went as data.addToCart; addToCartResultOf(block) narrows it to {status: 'resolved' | 'size_selection_needed' | 'error', ...}. A round that needs a size picked also emits a custom:addToCartResult block (ADD_TO_CART_RESULT_BLOCK).
thamyr.onResponse((response) => {
  if (response.type !== ThamyrResponseType.CHAPTER_EVENT) return;
  const labels = receptorsOf(response.chapter);
  for (const block of response.chapter.blocks ?? []) {
    const result = addToCartResultOf(block);
    if (result?.status === 'size_selection_needed') showSizePicker(result.availableSizes);
  }
});

Block Patches

An interaction a block's rive content declares in patchOn, sent with the block's key, does not compose a new round: the server answers with a block-patch response that re-sets the animation already on screen. values, inputs and textRuns hold only what changed; fire lists the triggers to play once, in order (input:<name> or vm:<name>).

applyBlockPatch(chapter, patch) returns the round with the patch merged into the animation it names (patch.contentId, a dotted path into block.data), or the same chapter when it holds no such block. The React and React Native stores apply it to the history for you; the patch itself still reaches onResponse, which is where to play fire, so triggers never replay from state.

thamyr.onResponse((response) => {
  if (response.type !== ThamyrResponseType.BLOCK_PATCH) return;
  round = applyBlockPatch(round, response.data);
  player.fire(response.data.fire);
});

Rendering Blocks

Thamyr responds with different types of content blocks based on the input events. You can render these blocks in your UI using a switch-case structure or dynamic rendering.

In the provided code, blocks are rendered based on their type. For example:

  • Text Block: Displayed as plain text.
  • Image Block: Displayed as an image.
  • Video Block: Displayed as a video.

Here's an example of how to render different block types:

const BlockRenderer = ({ block }) => {
    switch (block.type) {
        case "demosthenesResponse":
            return <TextBlock data={block.data} />;
        case "image":
            return <ImageBlock data={block.data} />;
        case "video":
            return <VideoBlock data={block.data} />;
        case "gallery":
            return <GalleryNew data={block.data} />;
        default:
            return <div>Unsupported block type: {block.type}</div>;
    }
};

Full Example of Block Rendering in Action

return (
    <div className="App">
        <div>
            {chapters.length > 0 ? (
                chapters.map((item, idx) => (
                    item.status === ChapterStatus.understandingQuery ? (
                        <p key={idx + 1}>Loading...</p>
                    ) : (
                        <div key={idx + 1}>
                            {item.blocks?.map(block => (
                                <BlockRenderer key={block.id} block={block} />
                            ))}
                        </div>
                    )
                ))
            ) : <p key={0}></p>}
        </div>
        <input
            type="text"
            value={inputText}
            onChange={e => setInputText(e.target.value)}
            placeholder="Type your question"
            onKeyDown={handleKeyDown}
        />
    </div>
);

(ChapterStatus is exported from @callimacus/thamyr.)

Example of a Block Component (ImageBlock)

const ImageBlock = ({ data }) => {
    return <img src={data.url} alt={data.content} className="rounded-lg shadow-lg" />;
};

export default ImageBlock;

API Namespaces

The Thamyr SDK organizes its APIs into logical namespaces for better code organization and discoverability.

Available Namespaces

  • content - Pages, topics, and documents
  • stories - Story sharing
  • products - Product recommendations and similarity
  • whisper - Whisper messages
  • analytics - Event tracking and analytics
  • cart - Shopping cart operations
  • skesis - Similar and related skesis items
  • sitemap - Site map retrieval

The content, stories, products, whisper, analytics, cart and skesis methods mirror the platform API one-to-one: each takes a single parameters object (parameterless reads like cart.getCart take none), resolves {data, error, response}, and new endpoints appear as methods automatically with SDK releases. The sitemap namespace keeps its classic signature (a promise that resolves to the value directly).

Working with Results

Platform API calls resolve {data, error, response} and never throw on HTTP errors — check error (or response.status) instead of wrapping calls in try/catch:

const thamyr = new Thamyr({
    clientId: import.meta.env.VITE_CALLIMACUS_CLIENT_ID,
});

const { data: page, error } = await thamyr.content.getPage({ language: 'en', id: 'home' });

if (error) {
    console.error('Could not load page:', error);
} else {
    renderPage(page);
}

"Not found" is a value too: on a 404, data is undefined, error carries the body and response.status === 404; on a network failure response is undefined. If you prefer exceptions, opt in per call with the second options argument (the first on parameterless methods such as cart.getCart): the result narrows to { data, request, response } and the rejection is a ThamyrHttpError (since 4.1.0), an Error with status, body, response and request:

import { ThamyrHttpError } from '@callimacus/thamyr';

const { data: page } = await thamyr.content.getPage({ language: 'en', id: 'home' }, { throwOnError: true });

try {
    await thamyr.cart.addItemToCart({ productId }, { throwOnError: true });
} catch (error) {
    if (error instanceof ThamyrHttpError && error.status === 500) retryOnce();
}

Content API

Access pages, topics, and documents. language is required — pass your locale explicitly:

// Get a page by ID
const { data: page } = await thamyr.content.getPage({ language: 'en', id: 'home' });

// List topics
const { data: topics } = await thamyr.content.listTopics({ language: 'en' });

// Get the contents of a topic
const { data: contents } = await thamyr.content.getTopicContents({ language: 'en', idOrSlug: 'running' });

// Get a document by slug, group, or sitemap path
const { data: bySlug } = await thamyr.content.getDocumentBySlug({ language: 'en', slug: 'about-us' });
const { data: byGroup } = await thamyr.content.getDocumentByGroup({ language: 'en', groupId: 'group-42' });
const { data: byPath } = await thamyr.content.getDocumentByPath({ path: '/en/about-us' });

Stories API

Share a story and retrieve shared stories:

// Create a shareable link for a story
const { data: share } = await thamyr.stories.share({ id: 'story-123' });

// Get a shared story
const { data: shared } = await thamyr.stories.getShared({ id: 'share-id-123' });

Products API

Get product recommendations and similar items:

// Get similar products — deprecated in favour of thamyr.skesis.similar; stays for all of 4.x, removed only after a product-shaped skesis read exists
const { data: similar } = await thamyr.products.getSimilar({ productId: 'product-123', topK: 10 });

// Get product recommendations — deprecated: use thamyr.skesis.related
const { data: recommendations } = await thamyr.products.getRecommendations({ productId: 'product-123' });

Whisper API

Fetch the latest whisper for a story (a 404 simply means there is none yet):

const { data, response } = await thamyr.whisper.getLast({ storyId: 'story-123' });

if (response.status !== 404) {
    showWhisper(data?.whisper);
}

Analytics API

Track an event for a single content item:

void thamyr.analytics.trackEvent({
   storyId: 'story-123',
   eventName: 'content_click',
   content: 'doc-42',
});

Track a “generic” event (no content id) — omit content if you don’t have content associated with the event:

void thamyr.analytics.trackEvent({
   storyId: 'story-123',
   eventName: 'button_click',
   payload: { buttonId: 'cta-primary', timestamp: Date.now() },
});

Track both content and custom payload:

void thamyr.analytics.trackEvent({
   storyId: 'story-123',
   eventName: 'content_click',
   content: 'doc-42',
   payload: {
      source: 'carousel',
      position: 2,
      highlighted: true,
   },
});

Cart API

Manage shopping cart operations. Like every contract namespace, cart methods take a flat parameters object and resolve the {data, error, response} envelope — the basket is data, and HTTP failures arrive as error, never as a rejection. The basket shape is Salesforce's own published SFCC schema (Basket).

// Get current cart
const {data: cart, error} = await thamyr.cart.getCart();

// Add item to cart
const {data: updated} = await thamyr.cart.addItemToCart({productId: 'product-123'});

// Remove item from cart
await thamyr.cart.removeItemFromCart({productId: 'product-123'});

// Update product quantity
await thamyr.cart.updateProductQuantity({productId: 'product-123', quantity: 5});

Skesis and Sitemap APIs

// Similar and related skesis items
const { data: similar } = await thamyr.skesis.similar({ anchorId: 'item-123', topK: 4 });
const { data: related } = await thamyr.skesis.related({ anchorId: 'item-123' });

// Full site map
const sitemap = await thamyr.sitemap.getSitemap();

Upgrading from 1.x, 2.x or 3.x

The wire protocol has not changed since v1: any major talks to the same server, and every break is compile-time except three things the guide calls out: error handling, retries, and the constructor rejecting an empty or relative endpoint at startup (4.0.2).

FromWhat breaksWhere
1.x@callimacus/common imports, accessToken, the flat wrappers, positional REST arguments, axios-style errorsComing from v1
2.xthamyr.cart.* positional arguments and bare Basket results, CartAPIMigrating to v3
3.xSkesisAPI, positional skesis.*, products.* deprecated (kept through 4.x), endpoint validated (4.0.2), basketId required (4.0.3)Migrating to v4

The same guide ships inside this package as MIGRATION.md, next to CHANGELOG.md and CHANGELOG.core.md (the docs site needs a Callimacus account).


版本与发布记录

@callimacus/thamyr 共发布 39 个版本,首次发布于 2026/09/29;过去 12 个月发布了 39 个版本。

版本发布日期距今
4.23.5latest2026/11/09今天
4.23.42026/11/072 天前
4.23.32026/11/062 天前
4.23.22026/11/063 天前
4.23.12026/11/063 天前
4.23.02026/11/053 天前

关于 @callimacus/thamyr 的常见问题

@callimacus/thamyr 是什么?

JavaScript package for Thamyr

如何安装 @callimacus/thamyr?

在项目目录运行 npm install @callimacus/thamyr,或使用 pnpm add / yarn add / bun add。

@callimacus/thamyr 支持 TypeScript 吗?

支持。包内自带 TypeScript 类型声明(package.json 声明了 types 入口),安装后即可获得类型提示,无需再安装 @types 包。

@callimacus/thamyr 支持 ESM 还是 CommonJS?

以 ES Module 形式发布,推荐使用 import 引入。在 CommonJS 项目中,Node.js 20.19+ / 22.12+ 可以直接 require() 不含顶层 await 的 ES Module,更早的版本需要改用动态 import()。

@callimacus/thamyr 可以商用吗?

@callimacus/thamyr 使用 SEE LICENSE IN LICENSE.md 许可。许可条款写在包内的独立文件中,请阅读该文件确认可否商用。以上为概括,不构成法律意见,请以许可原文为准。

@callimacus/thamyr 的下载量有多少?

根据 npm 官方下载统计,@callimacus/thamyr 在 2026/10/09 – 2026/11/07 这 30 天内被下载 6,486 次,日均约 216 次。下载量会把 CI 构建、镜像同步和作为其他包依赖的安装一并计入,适合横向比较热度,不能直接等同于使用人数。

@callimacus/thamyr 还在维护吗?

最新版本 4.23.5 发布于 2026/11/09(今天);至今共发布 39 个版本,其中过去 12 个月发布了 39 个;首次发布于 2026/09/29。发布频率只是参考信号:功能稳定的工具可能很少需要更新,建议结合源码仓库的 issue 和提交记录判断。