Skip to content

DynamoDBChatMessageHistory does not correctly serialize json messages #8003

Open
@arash2060

Description

@arash2060

Checked other resources

  • I added a very descriptive title to this issue.
  • I searched the LangChain.js documentation with the integrated search.
  • I used the GitHub search to find a similar question and didn't find it.
  • I am sure that this is a bug in LangChain.js rather than my code.
  • The bug is not resolved by updating to the latest stable version of LangChain (or the specific integration package).

Example Code

The following code:

// tests/dynamo-history.integration.test.ts // Renamed to indicate integration test

import {afterEach, beforeEach, describe, expect, it} from '@jest/globals';
import {AIMessage, BaseMessage, HumanMessage} from "@langchain/core/messages";
import {v4 as uuidv4} from "uuid";

// AWS SDK v3 imports for direct interaction in tests
import {DynamoDBClient} from "@aws-sdk/client-dynamodb";
import {DeleteCommand, DynamoDBDocumentClient, GetCommand} from "@aws-sdk/lib-dynamodb";
import {DynamoDBChatMessageHistory} from '@langchain/community/stores/message/dynamodb';

// --- Configuration ---
// Ensure these environment variables are set correctly for your test table/region
const TEST_TABLE_NAME = "TEST_TABLE"; // Use a dedicated test table name
const TEST_REGION = process.env.AWS_REGION || "us-east-1";

if (!process.env.DYNAMODB_HISTORY_TABLE_TEST) {
    console.warn(`TEST_TABLE env var not set. Using default: ${TEST_TABLE_NAME}. Ensure this table exists.`);
}

// --- Real AWS SDK Client for Test Setup/Verification/Cleanup ---
// Uses default credential provider chain
const realDdbClient = new DynamoDBClient({ region: TEST_REGION });
const realDocClient = DynamoDBDocumentClient.from(realDdbClient);

// Helper to create history instance pointing to the real table
const createHistory = (sessionId: string) => {
    return new DynamoDBChatMessageHistory({
        tableName: TEST_TABLE_NAME,
        sessionId: sessionId,
        partitionKey: 'pk',
        sortKey: 'sk',
        // Config for the *internal* client used by the history class
        config: {
            region: TEST_REGION,
        },
    });
};

// Define the sort key value the library likely uses or expects
// Adjust if needed based on library specifics or explicit configuration
const ACTUAL_SORT_KEY_VALUE = 'sk';

// Helper function to clean up item after test using the REAL client
const cleanupItem = async (sessionId: string) => {
    try {
        await realDocClient.send(new DeleteCommand({
            TableName: TEST_TABLE_NAME,
            Key: { pk: sessionId, sk: ACTUAL_SORT_KEY_VALUE },
        }));
        // console.log(`Cleaned up item for sessionId: ${sessionId}`);
    } catch (error) {
        console.error(`Error cleaning up sessionId ${sessionId}:`, error);
        // Don't fail test run due to cleanup error, but log it
    }
};

// --- Test Suite ---
describe("DynamoDBChatMessageHistory Integration Test", () => {
    let currentSessionId: string;

    beforeEach(() => {
        // Generate a unique sessionId for each test to ensure isolation
        currentSessionId = uuidv4();
    });

    afterEach(async () => {
        // Clean up the item created during the test
        if (currentSessionId) {
            await cleanupItem(currentSessionId);
        }
    });

    it("should instantiate correctly (no AWS call)", () => {
        const historyStore = createHistory(currentSessionId);
        expect(historyStore).toBeInstanceOf(DynamoDBChatMessageHistory);
    });

    it("should get empty messages if no history exists in DB", async () => {
        const historyStore = createHistory(currentSessionId);
        // Ensure item doesn't exist (handled by afterEach generally)
        const messages = await historyStore.getMessages();
        expect(messages).toEqual([]);
    });

    it("should add messages and store them in DynamoDB", async () => {
        const historyStore = createHistory(currentSessionId);
        const messagesToAdd: BaseMessage[] = [
            new HumanMessage({
                content: [
                    {
                        type: "text",
                        text: `Hi! I'm human` // Uses domString variable
                    }
                ]
            }),
            new AIMessage("Hi there! I'm real!"),
        ];

        // --- Act ---
        await historyStore.addMessages(messagesToAdd);

        // --- Assert ---
        // Verify by fetching directly from DynamoDB
        const { Item } = await realDocClient.send(new GetCommand({
            TableName: TEST_TABLE_NAME,
            Key: { pk: currentSessionId, sk: 'sk' },
        }));

        console.log(Item);

        expect(Item).toBeDefined();
        expect(Item?.pk).toBe(currentSessionId);
        expect(Item?.sk).toBe('sk');
        expect(Item?.messages).toBeDefined();
    });
});

Error Message and Stack Trace (if applicable)

Error: Error adding messages: UnknownError

at DynamoDBChatMessageHistory.addMessages (C:\Users\aaras\Dropbox\startup\website_augmenter\similar_extensions\utterXAmplifyBackend\node_modules\.pnpm\@[email protected]_c3b7e1eec298838f464006b49002acf9\node_modules\@langchain\community\dist\stores\message\dynamodb.cjs:210:19)
at processTicksAndRejections (node:internal/process/task_queues:105:5)
at Object.<anonymous> (C:\Users\aaras\Dropbox\startup\website_augmenter\similar_extensions\utterXAmplifyBackend\tests\dynamo-history-issue.test.ts:101:9)

Description

This works with HumanMessage("I'm human"), the issue is serialization.

System Info

[email protected] | MIT | deps: 12 | versions: 316
Typescript bindings for langchain
https://github.com/langchain-ai/langchainjs/tree/main/langchain/

keywords: llm, ai, gpt3, chain, prompt, prompt engineering, chatgpt, machine learning, ml, openai, embeddings, vectorstores

dist
.tarball: https://registry.npmjs.org/langchain/-/langchain-0.3.21.tgz
.shasum: 5f41f9e11f7f087b1883cc579c2f8e11597d6e4f
.integrity: sha512-fpor/V/xJRoLM7s1yQGlUE6aZIe6LLm7ciZcstNbBUYdFpCSigvADsW2cqlBCdbMXJxb5I/z9jznjGnmciJ+aw==
.unpackedSize: 2.9 MB

dependencies:
@langchain/openai: >=0.1.0 <0.6.0 js-yaml: ^4.1.0 openapi-types: ^12.1.3 yaml: ^2.2.1
@langchain/textsplitters: >=0.0.0 <0.2.0 jsonpointer: ^5.0.1 p-retry: 4 zod-to-json-schema: ^3.22.3
js-tiktoken: ^1.0.12 langsmith: >=0.2.8 <0.4.0 uuid: ^10.0.0 zod: ^3.22.4

maintainers:

dist-tags:
latest: 0.3.21 next: 0.3.2-rc.0 tag-for-publishing-older-releases: 0.2.20

Metadata

Metadata

Assignees

No one assigned

    Labels

    auto:bugRelated to a bug, vulnerability, unexpected error with an existing featurecommunityIssues related to `@langchain/community`

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions