# AI SDK အသုံးပြု၍ Web UI တည်ဆောက်ခြင်း - Tutorial

> Source: <https://gist.github.com/eimg/f374804f3b68de1e3d3e82f6bae6e52e>
> Published: 2026-07-17 06:52:34+00:00

[ပြီးခဲ့တဲ့ သင်ခန်းစာမှာ](https://gist.github.com/eimg/3856a76ec56848a0054518b09a1b804f) TypeScript, Vercel AI SDK နဲ့ OpenRouter တို့ကို အသုံးပြုပြီး AI agent တစ်ခုကို တည်ဆောက်ခဲ့ကြပါတယ်။

ကျွန်တော်တို့ရဲ့ Agent က -

- Language model ခေါ်ယူခြင်း
- Tool များအသုံးပြုခြင်း
- Agent Loop လုပ်ဆောင်ခြင်း
- Memory ယာယီသိမ်းဆည်းခြင်း
- Subagent များအသုံးပြုခြင်း
- Sequential Tasks များလုပ်ဆောင်ခြင်း
- Parallel Tasks များလုပ်ဆောင်ခြင်း

တို့ကို လုပ်ဆောင်နိုင်ခဲ့ပါတယ်။

ဒါပေမဲ့ အဲ့ဒီ Agent ဟာ Terminal ထဲမှာပဲ အလုပ်လုပ်တာ ဖြစ်ပါတယ်။

ဒီ သင်ခန်းစာမှာတော့ အဲ့ဒီ Agent ကို Web App အသေးလေးတစ်ခုအဖြစ် ပြောင်းလဲတည်ဆောက်သွားမှာ ဖြစ်ပါတယ်။

သင်ခန်းစာ ပြီးဆုံးသွားတဲ့အခါ ကျွန်တော်တို့ရဲ့ App ဟာ -

- Streaming responses
- React Chat interface
- Conversation history
- Multiple chat sessions
- Old conversations
- Creating new conversations
- Basic state management
- Better error handling
- Stopping a streaming response

စတဲ့ အခြေခံလုပ်ဆောင်ချက် ပြည့်ပြည့်စုံစုံကို ရရှိသွားမှာ ဖြစ်ပါတယ်။

အသုံးပြုသွားမယ့် နည်းပညာတွေကတော့ အရင်အတိုင်းပဲ -

- TypeScript
- npm
- Vercel AI SDK
- OpenRouter
- Zod

Web APP အတွက်တော့ Next.js ကို အသုံးပြုသွားမှာပါ။

Database တွေဘာတွေတော့ မသုံးသေးဘဲ Conversation တွေကို JSON ဖိုင်လေးတွေအနေနဲ့ပဲ သိမ်းဆည်းသွားပါမယ်။

အရင်တုန်းက Architecture က ဒီလိုပါ -

```
User
  ↓
Terminal
  ↓
Agent
  ↓
OpenRouter
  ↓
Language model
```

အသစ်ပြောင်းလဲမယ့် Architecture ကတော့ ဒီလို ဖြစ်သွားပါမယ် -

```
Browser
  ↓
React chat interface
  ↓
Next.js API route
  ↓
AI agent
  ↓
OpenRouter
  ↓
Language model
```

File storage လည်း ထည့်သွင်းပါမယ် -

```
Browser
  ↓
Chat API
  ↓
Agent
  ↓
JSON conversation files
```

Language model က JSON ဖိုင်တွေကို တိုက်ရိုက်ဖတ်တာ ရေးတာ မလုပ်ပါဘူး။ ကျွန်တော်တို့ရဲ့ TypeScript ကုဒ်ကပဲ သိမ်းဆည်းတဲ့အပိုင်းကို လုပ်သွားမှာပါ။

Next.js application အသစ်တစ်ခုကို အခုလို တည်ဆောက်ပါ -

```
npx create-next-app@latest my-agent-web
```

Setup လုပ်တဲ့အခါ မေးခွန်းတွေ မေးလာရင် အောက်ပါအတိုင်း ရွေးချယ်ပေးပါ -

```
Would you like to use the recommended Next.js defaults? 

Yes, use recommended defaults
```

ပြီးရင် Project directory ထဲကို ဝင်ပါ -

```
cd my-agent-web
```

လိုအပ်တဲ့ AI packages တွေကို Install လုပ်ပါ -

```
npm install ai @ai-sdk/react @openrouter/ai-sdk-provider zod
```

ဒီ Packages တွေရဲ့ အဓိကလုပ်ဆောင်ချက်တွေကတော့ -

`ai`

: Vercel AI SDK ရဲ့ ပင်မ Package ဖြစ်ပါတယ်။`@ai-sdk/react`

:`useChat()`

လိုမျိုး React hooks တွေ ပါဝင်ပါတယ်။`@openrouter/ai-sdk-provider`

: AI SDK နဲ့ OpenRouter ကို ချိတ်ဆက်ပေးပါတယ်။`zod`

: Tool input တွေကို စစ်ဆေးဖို့ အသုံးပြုပါတယ်။

Project ရဲ့ အပြင်ဘက်ဆုံး (Root) မှာ `.env.local`

ဖိုင်တစ်ခု ဖန်တီးပြီး အောက်ပါအတိုင်း ရေးထည့်ပါ -

```
OPENROUTER_API_KEY=your-openrouter-api-key
OPENROUTER_MODEL=xiaomi/mimo-v2.5
```

OpenRouter Model ကို မိမိနှစ်သက်ရာ အခြား Model နဲ့လည်း အစားထိုးနိုင်ပါတယ်။ နောက်ပိုင်း Calculator tool ကို အသုံးပြုမှာဖြစ်လို့ ရွေးချယ်လိုက်တဲ့ Model က Tool calling ရဖို့တော့ လိုပါတယ်။

API Key ကို Client-side React ကုဒ်တွေထဲမှာ မထည့်မိဖို့ကို သတိပြုပါ။ Server-side ဘက်မှာပဲ ရှိရပါမယ်။

ကျွန်တော်တို့ တဖြည်းဖြည်းချင်း တည်ဆောက်သွားမယ့် Folder Structure က အောက်ပါအတိုင်း ဖြစ်ပါတယ် -

```
my-agent-web/
├── app/
│   ├── api/
│   │   ├── chat/
│   │   │   └── route.ts
│   │   └── chats/
│   │       └── route.ts
│   ├── chat/
│   │   ├── [id]/
│   │   │   └── page.tsx
│   │   └── page.tsx
│   ├── globals.css
│   ├── layout.tsx
│   └── page.tsx
├── components/
│   ├── chat-app.tsx
│   ├── chat-input.tsx
│   ├── chat-sidebar.tsx
│   └── message-list.tsx
├── lib/
│   ├── agent.ts
│   ├── chat-store.ts
│   ├── message-utils.ts
│   └── tools.ts
├── scripts/
│   └── stream-test.ts
├── .data/
│   └── chats/
├── .env.local
├── package.json
└── tsconfig.json
```

`.data/chats`

folder ဟာ ပထမဆုံး Chat History ကို သိမ်းဆည်းတဲ့အခါ အလိုအလျောက် ဖန်တီးသွားမှာပါ။

ပြီးခဲ့တဲ့ သင်ခန်းစာမှာ `generateText()`

ကို အသုံးပြုခဲ့ပါတယ် -

``` js
const result = await generateText({
  model,
  prompt: "Explain AI agents.",
});

console.log(result.text);
```

`generateText()`

ဟာ စာတွေ အကုန်လုံး ထုတ်ပေးလို့ ပြီးတဲ့အထိ စောင့်ပါတယ်။

Flow က ဒီလိုပါ -

```
Send prompt
  ↓
Wait for complete generation
  ↓
Receive complete answer
  ↓
Display answer
```

Streaming ရဲ့ အလုပ်လုပ်ပုံကတော့ ကွဲပြားပါတယ် -

```
Send prompt
  ↓
Receive first piece
  ↓
Display it
  ↓
Receive next piece
  ↓
Display it
  ↓
Continue until complete
```

Streaming ဟာ Model ကို ပိုမြန်မြန် စာထုတ်ပေးအောင် လုပ်တာမဟုတ်ပါဘူး။ User အနေနဲ့ အဖြေအပြည့်အစုံ မပြီးခင်မှာကတည်းက စာတွေကို တဖြည်းဖြည်းချင်း မြင်တွေ့နိုင်အောင် လုပ်ပေးတာ ဖြစ်ပါတယ်။

Web interface မတည်ဆောက်ခင် Streaming သီးသန့် အလုပ်လုပ်ပုံကို အရင်စမ်းကြည့်ရအောင်။

`scripts/stream-test.ts`

ဆိုပြီး ဖိုင်တစ်ခု ဖန်တီးပါ (scripts folder မရှိသေးရင် အသစ်ဆောက်ပါ)။ ပြီးတဲ့အခါ ဒီကုဒ်ကို ထည့်ပါ -

``` js
import { createOpenRouter } from "@openrouter/ai-sdk-provider";
import { streamText } from "ai";

async function main() {
  const apiKey = process.env.OPENROUTER_API_KEY;
  const modelName = process.env.OPENROUTER_MODEL;

  if (!apiKey) {
    throw new Error("OPENROUTER_API_KEY is missing.");
  }

  if (!modelName) {
    throw new Error("OPENROUTER_MODEL is missing.");
  }

  const openrouter = createOpenRouter({
    apiKey,
  });

  const result = streamText({
    model: openrouter(modelName),
    prompt: "Explain streaming responses in simple terms.",
  });

  for await (const textPart of result.textStream) {
    process.stdout.write(textPart);
  }

  process.stdout.write("\n");
}

main().catch((error) => {
  console.error(error);
  process.exitCode = 1;
});
```

ဒီဖိုင်ကို သီးသန့် Run လို့ရအောင် `tsx`

ကို install လုပ်ပါ -

```
npm install -D tsx
```

`package.json`

ထဲက `scripts`

မှာ `stream-test`

ကို သွားထည့်ပေးပါ (ရှိပြီးသား Next.js script တွေကို မဖျက်ပါနဲ့) -

```
{
  "scripts": {
    "dev": "next dev",
    "build": "next build",
    "start": "next start",
    "lint": "next lint",
    "stream-test": "tsx --env-file=.env.local scripts/stream-test.ts"
  }
}
```

အခုလို Run ကြည့်ပါ -

```
npm run stream-test
```

စာတွေ တဖြည်းဖြည်းချင်း Stream အနေနဲ့ ထွက်လာတာကို တွေ့ရပါလိမ့်မယ်။

အဓိကကုဒ်ကတော့ `streamText`

ကို အသုံးပြုထားတာပါပဲ -

``` js
const result = streamText({
  model,
  prompt,
});
```

ပြီးတော့ ရလာတဲ့ အပိုင်းအစလေးတွေကို အခုလို loop ပတ်ပြီး ထုတ်ယူပါတယ် -

``` js
for await (const textPart of result.textStream) {
  process.stdout.write(textPart);
}
```

Agent နဲ့သက်ဆိုင်တဲ့ Logic တွေကို API route ထဲမှာ အကုန်မရေးဘဲ သီးသန့်ခွဲထုတ်ပါမယ်။

`lib/agent.ts`

ကို ဖန်တီးပြီး အောက်ပါအတိုင်း ရေးပါ -

``` js
import { createOpenRouter } from "@openrouter/ai-sdk-provider";

const apiKey = process.env.OPENROUTER_API_KEY;
const modelName = process.env.OPENROUTER_MODEL;

if (!apiKey) {
  throw new Error("OPENROUTER_API_KEY is missing.");
}

if (!modelName) {
  throw new Error("OPENROUTER_MODEL is missing.");
}

const openrouter = createOpenRouter({
  apiKey,
});

export const agentModel = openrouter(modelName);

export const agentSystemPrompt = `
You are a helpful AI assistant.

Explain ideas clearly and directly.
Use the calculator tool whenever accurate arithmetic is required.
After using a tool, explain the result to the user.

Do not claim that you performed an action unless a tool actually performed it.
`;
```

ဒီ module ထဲမှာ -

- Provider သတ်မှတ်ခြင်း
- Model ရွေးချယ်ခြင်း
- System instructions များ ပါဝင်ပါတယ်။

UI တွေ၊ HTTP request တွေ၊ သိမ်းဆည်းတဲ့အပိုင်းတွေကို ဒီထဲမှာ မထည့်ပါဘူး။ သီးခြားခွဲထားမှာပါ။

`lib/tools.ts`

ကို ဖန်တီးပြီး အောက်ပါအတိုင်း ရေးပါ -

``` js
import { tool } from "ai";
import { z } from "zod";

export const calculatorTool = tool({
  description:
    "Perform accurate arithmetic using two numbers. Use this tool when arithmetic is required.",

  inputSchema: z.object({
    operation: z
      .enum(["add", "subtract", "multiply", "divide"])
      .describe("The arithmetic operation to perform"),

    a: z.number().describe("The first number"),

    b: z.number().describe("The second number"),
  }),

  execute: async ({ operation, a, b }) => {
    switch (operation) {
      case "add":
        return {
          success: true,
          result: a + b,
        };

      case "subtract":
        return {
          success: true,
          result: a - b,
        };

      case "multiply":
        return {
          success: true,
          result: a * b,
        };

      case "divide":
        if (b === 0) {
          return {
            success: false,
            error: "Division by zero is not allowed.",
          };
        }

        return {
          success: true,
          result: a / b,
        };
    }
  },
});

export const agentTools = {
  calculator: calculatorTool,
};
```

Tools တွေကို Object တစ်ခုတည်းအနေနဲ့ ပေါင်းပြီး Export လုပ်ထားပါတယ် -

``` js
export const agentTools = {
  calculator: calculatorTool,
};
```

ဒီလိုလုပ်ထားတာက `streamText()`

မှာပဲဖြစ်ဖြစ်၊ Message တွေ Validate လုပ်တဲ့အခါမှာပဲဖြစ်ဖြစ် အလွယ်တကူ ထည့်သွင်းအသုံးပြုနိုင်ဖို့ ဖြစ်ပါတယ်။

`app/api/chat/route.ts`

ကို ဖန်တီးပြီး အောက်ပါအတိုင်း ရေးပါ -

``` js
import {
  convertToModelMessages,
  createIdGenerator,
  type InferUITools,
  stepCountIs,
  streamText,
  type UIDataTypes,
  type UIMessage,
  validateUIMessages,
} from "ai";

import {
  agentModel,
  agentSystemPrompt,
} from "@/lib/agent";

import { agentTools } from "@/lib/tools";

export const runtime = "nodejs";

type AgentUIMessage = UIMessage<
  never,
  UIDataTypes,
  InferUITools<typeof agentTools>
>;

type ChatRequestBody = {
  id?: string;
  messages?: AgentUIMessage[];
};

export async function POST(request: Request): Promise<Response> {
  try {
    const body = (await request.json()) as ChatRequestBody;

    if (!body.id) {
      return Response.json(
        {
          error: "Chat ID is required.",
        },
        {
          status: 400,
        },
      );
    }

    if (!Array.isArray(body.messages)) {
      return Response.json(
        {
          error: "Messages must be an array.",
        },
        {
          status: 400,
        },
      );
    }

    const validatedMessages = await validateUIMessages<AgentUIMessage>({
      messages: body.messages,
      tools: agentTools,
    });

    const modelMessages =
      await convertToModelMessages(validatedMessages);

    const result = streamText({
      model: agentModel,

      system: agentSystemPrompt,

      messages: modelMessages,

      tools: agentTools,

      stopWhen: stepCountIs(5),
    });

    return result.toUIMessageStreamResponse({
      originalMessages: validatedMessages,

      generateMessageId: createIdGenerator({
        prefix: "msg",
        size: 16,
      }),

      onError: (error) => {
        console.error("Streaming error:", error);

        return "The assistant could not complete the response.";
      },
    });
  } catch (error) {
    console.error("Chat request failed:", error);

    return Response.json(
      {
        error: "The chat request failed.",
      },
      {
        status: 500,
      },
    );
  }
}
```

အခုလောလောဆယ်တော့ History သိမ်းဆည်းတဲ့ အပိုင်းကို မထည့်ရသေးပါဘူး။

ဒီ API endpoint က -

- Message တွေကို လက်ခံတယ်
- မှန်မမှန် စစ်ဆေးတယ်
- UI message ကနေ Model message ကို ပြောင်းပေးတယ်
- Model ကို လှမ်းခေါ်တယ်
- Tool တွေ လိုအပ်ရင် run ပေးတယ်
- Browser ဆီကို Response ကို Streaming ပုံစံနဲ့ ပြန်ပို့ပေးပါတယ်။

Browser နဲ့ Model ဟာ Message format အမျိုးအစား မတူညီတာကို သုံးကြပါတယ်။

React UI က `UIMessage[]`

ကို သုံးပါတယ် -

```
type UIMessage = {
  id: string;
  role: "user" | "assistant" | "system";
  parts: UIMessagePart[];
};
```

Model ကတော့ `ModelMessage[]`

ကို သုံးပါတယ်။ ဒါကြောင့် `convertToModelMessages`

နဲ့ ပြောင်းလဲပေးရတာပါ -

``` js
const modelMessages = await convertToModelMessages(validatedMessages);
```

Flow က ဒီလိုပါ -

```
Browser UIMessage[]
  ↓
convertToModelMessages()
  ↓
ModelMessage[]
  ↓
Language model
```

နောက်ပိုင်း သိမ်းဆည်းတဲ့အခါမှာ UI ဖက်က လိုအပ်တဲ့ ဖွဲ့စည်းပုံတွေ ပါဝင်တဲ့ `UIMessage`

တွေကိုပဲ အဓိကထား သိမ်းဆည်းသွားမှာပါ။

Browser ကနေ Server ဆီ Data တွေ ပို့တဲ့အခါ မှန်ကန်တယ်လို့ အမြဲမယူဆသင့်ပါဘူး။ ဒီကုဒ်က ဝင်လာတဲ့ Message တွေကို စစ်ဆေးပေးပါတယ် -

``` js
const validatedMessages = await validateUIMessages<AgentUIMessage>({
  messages: body.messages,
  tools: agentTools,
});
```

Tool calls တွေ၊ Tool results တွေ၊ Metadata တွေ ပါလာတဲ့အခါ အမှားအယွင်းမရှိအောင် Validate လုပ်ပေးခြင်းက Tool information အမှားတွေ Model ဆီ ရောက်မသွားအောင် ကာကွယ်ပေးပါတယ်။

`streamText()`

ကနေ ပြန်ရလာတဲ့ Result ဟာ ရိုးရိုး JSON object မဟုတ်ပါဘူး။ Active stream တစ်ခု ဖြစ်ပါတယ်။
ဒါကို `useChat()`

က နားလည်တဲ့ HTTP response အဖြစ် ပြောင်းလဲပေးဖို့ ဒီ method ကို သုံးရတာပါ -

```
return result.toUIMessageStreamResponse();
```

`components/chat-app.tsx`

ကို ဖန်တီးပြီး အောက်ပါအတိုင်း ရေးပါ -

``` js
"use client";

import { useChat } from "@ai-sdk/react";
import {
  DefaultChatTransport,
  type UIMessage,
} from "ai";

import {
  type SubmitEvent,
  useState,
} from "react";

type ChatAppProps = {
  chatId: string;
  initialMessages?: UIMessage[];
};

export function ChatApp({
  chatId,
  initialMessages = [],
}: ChatAppProps) {
  const [input, setInput] = useState("");

  const {
    messages,
    sendMessage,
    status,
    error,
    stop,
  } = useChat({
    id: chatId,

    messages: initialMessages,

    transport: new DefaultChatTransport({
      api: "/api/chat",
    }),

    onError: (chatError) => {
      console.error("Chat error:", chatError);
    },
  });

  const isBusy =
    status === "submitted" ||
    status === "streaming";

  async function handleSubmit(
    event: SubmitEvent<HTMLFormElement>,
  ): Promise<void> {
    event.preventDefault();

    const message = input.trim();

    if (!message || isBusy) {
      return;
    }

    setInput("");

    await sendMessage({
      text: message,
    });
  }

  return (
    <main className="chat-page">
      <section className="chat-panel">
        <header className="chat-header">
          <div>
            <h1>My AI Agent</h1>
            <p>Chat ID: {chatId}</p>
          </div>

          {isBusy && (
            <button
              type="button"
              className="secondary-button"
              onClick={stop}
            >
              Stop
            </button>
          )}
        </header>

        <section className="message-list">
          {messages.length === 0 && (
            <div className="empty-chat">
              <h2>Start a conversation</h2>
              <p>
                Ask a question or request a calculation.
              </p>
            </div>
          )}

          {messages.map((message) => (
            <article
              key={message.id}
              className={`message message-${message.role}`}
            >
              <strong>
                {message.role === "user"
                  ? "You"
                  : "Assistant"}
              </strong>

              <div className="message-content">
                {message.parts.map((part, index) => {
                  if (part.type === "text") {
                    return (
                      <p key={`${message.id}-${index}`}>
                        {part.text}
                      </p>
                    );
                  }

                  return null;
                })}
              </div>
            </article>
          ))}

          {status === "submitted" && (
            <div className="status-message">
              Assistant is thinking...
            </div>
          )}
        </section>

        {error && (
          <div className="error-message">
            {error.message ||
              "Something went wrong while generating the response."}
          </div>
        )}

        <form
          className="chat-form"
          onSubmit={handleSubmit}
        >
          <textarea
            value={input}
            onChange={(event) =>
              setInput(event.target.value)
            }
            placeholder="Type your message..."
            rows={3}
            disabled={isBusy}
          />

          <button
            type="submit"
            disabled={!input.trim() || isBusy}
          >
            Send
          </button>
        </form>
      </section>
    </main>
  );
}
```

အဓိက အစိတ်အပိုင်းကတော့ ဒါပါ -

```
const {
  messages,
  sendMessage,
  status,
  error,
  stop,
} = useChat({ ... });
```

`useChat()`

က Client-side ရဲ့ Chat state ကို စီမံပေးပါတယ်။

`messages`

: လက်ရှိ ပြသပေးနေတဲ့ messages တွေ`sendMessage`

: User ရဲ့ Message ကို ပို့ပေးမယ့် Function`status`

: လက်ရှိ Request ရဲ့ အခြေအနေ`error`

: Error တက်ရင် ပြပေးမယ့် အချက်အလက်`stop`

: အလုပ်လုပ်နေတဲ့ Stream ကို ရပ်ပစ်မယ့် Function

Status တွေက အောက်ပါအတိုင်း ဖြစ်နိုင်ပါတယ် -

`ready`

: နောက်ထပ် Message လက်ခံဖို့ အသင့်ဖြစ်နေတဲ့ အချိန်`submitted`

: Request ပို့ပြီးပြီ၊ ဒါပေမဲ့ Response ပြန်မလာသေးတဲ့ အချိန်`streaming`

: Response တဖြည်းဖြည်းချင်း ဝင်လာနေတဲ့ အချိန်`error`

: Request ပို့တာ Fail ဖြစ်သွားတဲ့ အချိန်

UI Message တစ်ခုမှာ စာသားတွေကို တိုက်ရိုက်သိမ်းထားတာ မဟုတ်ဘဲ `parts`

အနေနဲ့ သိမ်းထားတာပါ -

``` js
{message.parts.map((part, index) => {
  if (part.type === "text") {
    return (
      <p key={`${message.id}-${index}`}>
        {part.text}
      </p>
    );
  }
  return null;
})}
```

နောက်ပိုင်းမှာ Tool calls တွေ၊ Results တွေလည်း ဒီ `parts`

ထဲမှာ ပါလာမှာ ဖြစ်ပေမဲ့ အခုလောလောဆယ်တော့ Text ကိုပဲ ပြပေးထားပါတယ်။

`app/page.tsx`

ကို ဖွင့်ပြီး အောက်ပါအတိုင်း အစားထိုးပါ -

``` js
import { ChatApp } from "@/components/chat-app";

export default function HomePage() {
  return (
    <ChatApp
      chatId="temporary-chat"
      initialMessages={[]}
    />
  );
}
```

အခုချိန်မှာ Persistence တို့၊ Multiple chats တို့ မပါသေးပါဘူး။ Streaming ကို စမ်းဖို့အတွက်ပဲ ဖြစ်ပါတယ်။

`app/globals.css`

ထဲက စာတွေကို အောက်ပါကုဒ်နဲ့ အစားထိုးလိုက်ပါ -

```
* { box-sizing: border-box; }
html, body { margin: 0; min-height: 100%; }
body { background: #f4f5f7; color: #202124; font-family: Arial, Helvetica, sans-serif; }
button, textarea { font: inherit; }
button { cursor: pointer; }
button:disabled { cursor: not-allowed; opacity: 0.6; }
.chat-page { min-height: 100vh; padding: 24px; }
.chat-panel { display: flex; flex-direction: column; width: min(900px, 100%); height: calc(100vh - 48px); margin: 0 auto; overflow: hidden; background: white; border: 1px solid #dfe1e5; border-radius: 12px; }
.chat-header { display: flex; align-items: center; justify-content: space-between; padding: 18px 20px; border-bottom: 1px solid #e5e7eb; }
.chat-header h1 { margin: 0; font-size: 20px; }
.chat-header p { margin: 4px 0 0; color: #6b7280; font-size: 12px; }
.message-list { flex: 1; overflow-y: auto; padding: 24px; }
.empty-chat { max-width: 480px; margin: 100px auto; text-align: center; }
.empty-chat h2 { margin-bottom: 8px; }
.empty-chat p { color: #6b7280; }
.message { max-width: 75%; margin-bottom: 18px; padding: 14px 16px; border-radius: 12px; line-height: 1.55; }
.message strong { display: block; margin-bottom: 6px; font-size: 13px; }
.message-user { margin-left: auto; background: #e8f0fe; }
.message-assistant { margin-right: auto; background: #f1f3f4; }
.message-content p { margin: 0; white-space: pre-wrap; }
.status-message { color: #6b7280; font-size: 14px; }
.error-message { margin: 0 20px 12px; padding: 12px; color: #991b1b; background: #fee2e2; border-radius: 8px; }
.chat-form { display: flex; gap: 12px; padding: 16px; border-top: 1px solid #e5e7eb; }
.chat-form textarea { flex: 1; resize: none; padding: 12px; border: 1px solid #cbd5e1; border-radius: 8px; }
.chat-form button, .secondary-button { padding: 10px 18px; color: white; background: #1f2937; border: 0; border-radius: 8px; }
.secondary-button { background: #64748b; }
npm run dev
```

`http://localhost:3000`

ကို ဖွင့်ပြီး စမ်းသပ်ကြည့်ပါ။

"Explain what an AI agent is." လို့ ရိုက်ထည့်ကြည့်ပါ၊ အဖြေတွေ တဖြည်းဖြည်း ထွက်လာတာ တွေ့ရပါမယ်။

ပြီးရင် "Calculate 48 multiplied by 17." လို့ ရိုက်ကြည့်ပါ၊ Tool ကို လှမ်းသုံးသွားတာကိုပါ တွေ့ရမှာပါ။

လက်ရှိ Flow က ဒီလို အလုပ်လုပ်နေတာပါ -

```
User types message
  ↓
useChat() sends UI messages
  ↓
POST /api/chat
  ↓
Messages are validated and converted
  ↓
streamText() calls the model
  ↓
Tool may execute
  ↓
Response streams to the browser
  ↓
useChat() updates the message
  ↓
React rerenders
```

အခုချိန်မှာ Browser ကပဲ Message တွေကို မှတ်ထားပေးတာဖြစ်လို့ Page ကို Refresh လုပ်လိုက်ရင် ပျောက်သွားမှာပါ။

ဒါကြောင့် Chat တွေကို JSON ဖိုင်တွေအနေနဲ့ `.data/chats/`

အောက်မှာ သိမ်းဆည်းပေးမယ့်စနစ် လိုအပ်ပါတယ်။ Database သုံးမယ့်အစား အလွယ်တကူ လေ့လာနိုင်ဖို့ File storage ကိုပဲ သုံးပါမယ်။

`lib/chat-store.ts`

ကို ဖန်တီးပြီး အောက်ပါအတိုင်း အပြည့်အစုံ ရေးထည့်ပါ -

``` js
import {
  createIdGenerator,
  type UIMessage,
} from "ai";

import {
  mkdir,
  readFile,
  readdir,
  rename,
  writeFile,
} from "node:fs/promises";

import path from "node:path";

export type StoredChat = {
  id: string;
  title: string;
  messages: UIMessage[];
  createdAt: string;
  updatedAt: string;
};

export type ChatSummary = {
  id: string;
  title: string;
  createdAt: string;
  updatedAt: string;
};

const createChatId = createIdGenerator({
  prefix: "chat",
  size: 16,
});

const chatDirectory = path.join(
  process.cwd(),
  ".data",
  "chats",
);

async function ensureChatDirectory(): Promise<void> {
  await mkdir(chatDirectory, {
    recursive: true,
  });
}

function getChatFilePath(chatId: string): string {
  if (!/^[a-zA-Z0-9_-]+$/.test(chatId)) {
    throw new Error("Invalid chat ID.");
  }

  return path.join(
    chatDirectory,
    `${chatId}.json`,
  );
}

function getTextFromMessage(
  message: UIMessage,
): string {
  return message.parts
    .filter(
      (
        part,
      ): part is Extract<
        UIMessage["parts"][number],
        { type: "text" }
      > => part.type === "text",
    )
    .map((part) => part.text)
    .join("")
    .trim();
}

function createChatTitle(
  messages: UIMessage[],
): string {
  const firstUserMessage = messages.find(
    (message) => message.role === "user",
  );

  if (!firstUserMessage) {
    return "New conversation";
  }

  const text = getTextFromMessage(
    firstUserMessage,
  );

  if (!text) {
    return "New conversation";
  }

  const maximumLength = 48;

  if (text.length <= maximumLength) {
    return text;
  }

  return `${text.slice(0, maximumLength)}…`;
}

async function writeJsonFile(
  filePath: string,
  value: unknown,
): Promise<void> {
  const temporaryPath = `${filePath}.tmp`;

  const json = JSON.stringify(
    value,
    null,
    2,
  );

  await writeFile(
    temporaryPath,
    json,
    "utf8",
  );

  await rename(
    temporaryPath,
    filePath,
  );
}

export async function createChat(): Promise<StoredChat> {
  await ensureChatDirectory();

  const now = new Date().toISOString();

  const chat: StoredChat = {
    id: createChatId(),
    title: "New conversation",
    messages: [],
    createdAt: now,
    updatedAt: now,
  };

  await writeJsonFile(
    getChatFilePath(chat.id),
    chat,
  );

  return chat;
}

export async function loadChat(
  chatId: string,
): Promise<StoredChat | null> {
  await ensureChatDirectory();

  try {
    const content = await readFile(
      getChatFilePath(chatId),
      "utf8",
    );

    return JSON.parse(content) as StoredChat;
  } catch (error) {
    const nodeError =
      error as NodeJS.ErrnoException;

    if (nodeError.code === "ENOENT") {
      return null;
    }

    throw error;
  }
}

export async function saveChat({
  chatId,
  messages,
}: {
  chatId: string;
  messages: UIMessage[];
}): Promise<StoredChat> {
  const existingChat =
    await loadChat(chatId);

  if (!existingChat) {
    throw new Error(
      `Chat "${chatId}" does not exist.`,
    );
  }

  const updatedChat: StoredChat = {
    ...existingChat,
    title: createChatTitle(messages),
    messages,
    updatedAt: new Date().toISOString(),
  };

  await writeJsonFile(
    getChatFilePath(chatId),
    updatedChat,
  );

  return updatedChat;
}

export async function listChats(): Promise<
  ChatSummary[]
> {
  await ensureChatDirectory();

  const filenames = await readdir(
    chatDirectory,
  );

  const chatFiles = filenames.filter(
    (filename) =>
      filename.endsWith(".json") &&
      !filename.endsWith(".tmp"),
  );

  const chats = await Promise.all(
    chatFiles.map(async (filename) => {
      try {
        const content = await readFile(
          path.join(chatDirectory, filename),
          "utf8",
        );

        const chat =
          JSON.parse(content) as StoredChat;

        return {
          id: chat.id,
          title: chat.title,
          createdAt: chat.createdAt,
          updatedAt: chat.updatedAt,
        } satisfies ChatSummary;
      } catch (error) {
        console.error(
          `Could not read ${filename}:`,
          error,
        );

        return null;
      }
    }),
  );

  return chats
    .filter(
      (
        chat,
      ): chat is ChatSummary =>
        chat !== null,
    )
    .sort(
      (first, second) =>
        new Date(second.updatedAt).getTime() -
        new Date(first.updatedAt).getTime(),
    );
}
```

ဒီဖိုင်ထဲမှာ Chat အသစ်တည်ဆောက်တာ၊ ဖတ်တာ၊ သိမ်းတာ၊ စာရင်းထုတ်တာတွေကို လုပ်ဆောင်ပေးမယ့် Function တွေ ပါဝင်ပါတယ်။ JSON ဖိုင်ကို Save တဲ့အခါ ယာယီ `.tmp`

ဖိုင်တစ်ခု အရင်ရေးပြီးမှ အမည်ပြောင်းတဲ့နည်း (Safe Write) ကို အသုံးပြုထားပါတယ်။

`app/api/chat/route.ts`

ကို ဖွင့်ပြီး `loadChat`

, `saveChat`

ကို import လုပ်ကာ `POST`

function ထဲက `onFinish`

မှာ သိမ်းဆည်းမယ့် လုပ်ဆောင်ချက် ထည့်ပါမယ်။ ဖိုင်အပြည့်အစုံကို အောက်ပါအတိုင်း အစားထိုးလိုက်ပါ -

``` js
import {
  convertToModelMessages,
  createIdGenerator,
  type InferUITools,
  stepCountIs,
  streamText,
  type UIDataTypes,
  type UIMessage,
  validateUIMessages,
} from "ai";

import {
  agentModel,
  agentSystemPrompt,
} from "@/lib/agent";

import {
  loadChat,
  saveChat,
} from "@/lib/chat-store";

import { agentTools } from "@/lib/tools";

export const runtime = "nodejs";

type AgentUIMessage = UIMessage<
  never,
  UIDataTypes,
  InferUITools<typeof agentTools>
>;

type ChatRequestBody = {
  id?: string;
  messages?: AgentUIMessage[];
};

export async function POST(
  request: Request,
): Promise<Response> {
  try {
    const body =
      (await request.json()) as ChatRequestBody;

    if (!body.id) {
      return Response.json(
        { error: "Chat ID is required." },
        { status: 400 },
      );
    }

    if (!Array.isArray(body.messages)) {
      return Response.json(
        { error: "Messages must be an array." },
        { status: 400 },
      );
    }

    const storedChat = await loadChat(body.id);

    if (!storedChat) {
      return Response.json(
        { error: "Chat was not found." },
        { status: 404 },
      );
    }

    const validatedMessages = await validateUIMessages<AgentUIMessage>({
      messages: body.messages,
      tools: agentTools,
    });

    const modelMessages = await convertToModelMessages(validatedMessages);

    const result = streamText({
      model: agentModel,
      system: agentSystemPrompt,
      messages: modelMessages,
      tools: agentTools,
      stopWhen: stepCountIs(5),
    });

    return result.toUIMessageStreamResponse({
      originalMessages: validatedMessages,

      generateMessageId: createIdGenerator({
        prefix: "msg",
        size: 16,
      }),

      onFinish: async ({ messages }) => {
        try {
          await saveChat({
            chatId: body.id!,
            messages,
          });
        } catch (error) {
          console.error("Could not save chat:", error);
        }
      },

      onError: (error) => {
        console.error("Streaming error:", error);
        return "The assistant could not complete the response.";
      },
    });
  } catch (error) {
    console.error("Chat request failed:", error);
    return Response.json(
      { error: "The chat request failed." },
      { status: 500 },
    );
  }
}
```

`onFinish`

ထဲမှာ ပါလာတဲ့ `messages`

တွေက အစအဆုံး အကုန်ပါဝင်တဲ့အတွက် အဲ့ဒါကို တိုက်ရိုက် Save လုပ်လိုက်ရုံပါပဲ။ Streaming ပြီးတာနဲ့ သိမ်းလိုက်တဲ့ သဘောပါ။

`app/chat/page.tsx`

ဖိုင်ဖန်တီးပြီး အခုလို ရေးပါ -

``` js
import { redirect } from "next/navigation";
import { createChat } from "@/lib/chat-store";

export const dynamic = "force-dynamic";

export default async function NewChatPage() {
  const chat = await createChat();
  redirect(`/chat/${chat.id}`);
}
```

`/chat`

ကို ဝင်လိုက်တာနဲ့ Chat အသစ်တစ်ခု ဆောက်ပြီး ချက်ချင်း Redirect လုပ်သွားမှာပါ။

`app/page.tsx`

ကို အခုလို ပြင်ပါ -

``` js
import { redirect } from "next/navigation";

export default function HomePage() {
  redirect("/chat");
}
```

`app/chat/[id]/page.tsx`

ကို ဖန်တီးပြီး အခုလို ရေးပါ -

``` js
import { notFound } from "next/navigation";
import { ChatApp } from "@/components/chat-app";
import {
  listChats,
  loadChat,
} from "@/lib/chat-store";

export const dynamic = "force-dynamic";

type ChatPageProps = {
  params: Promise<{
    id: string;
  }>;
};

export default async function ChatPage({
  params,
}: ChatPageProps) {
  const { id } = await params;

  const [chat, chats] = await Promise.all([
    loadChat(id),
    listChats(),
  ]);

  if (!chat) {
    notFound();
  }

  return (
    <ChatApp
      chatId={chat.id}
      initialMessages={chat.messages}
      initialChats={chats}
    />
  );
}
```

ဒီနေရာမှာ `Promise.all`

ကိုသုံးပြီး လိုအပ်တဲ့ data နှစ်ခုကို Parallel ယူထားတာ သတိပြုပါ။

`components/chat-app.tsx`

မှာ `initialChats`

ထပ်ထည့်ပေးဖို့ လိုလာပါပြီ။ ခဏနေရင် Sidebar လည်း ထည့်ရမှာမို့ `ChatApp`

တစ်ခုလုံးကို နောက်ပိုင်းကျရင် အပြည့်အစုံ အစားထိုးပါမယ်။

`components/chat-sidebar.tsx`

ဖန်တီးပြီး အခုလို ရေးပါ -

``` python
"use client";

import Link from "next/link";
import type { ChatSummary } from "@/lib/chat-store";

type ChatSidebarProps = {
  activeChatId: string;
  chats: ChatSummary[];
};

export function ChatSidebar({
  activeChatId,
  chats,
}: ChatSidebarProps) {
  return (
    <aside className="chat-sidebar">
      <Link href="/chat" className="new-chat-button">
        + New chat
      </Link>

      <nav className="conversation-list">
        {chats.length === 0 && (
          <p className="no-conversations">
            No conversations yet.
          </p>
        )}

        {chats.map((chat) => (
          <Link
            key={chat.id}
            href={`/chat/${chat.id}`}
            className={
              chat.id === activeChatId
                ? "conversation-link conversation-link-active"
                : "conversation-link"
            }
          >
            <span className="conversation-title">{chat.title}</span>
            <span className="conversation-date">{formatDate(chat.updatedAt)}</span>
          </Link>
        ))}
      </nav>
    </aside>
  );
}

function formatDate(dateString: string): string {
  const date = new Date(dateString);
  return new Intl.DateTimeFormat(undefined, {
    month: "short",
    day: "numeric",
  }).format(date);
}
```

`components/message-list.tsx`

ဖန်တီးပြီး အောက်ပါအတိုင်း ရေးပါ -

``` python
"use client";

import type { UIMessage } from "ai";

type MessageListProps = {
  messages: UIMessage[];
  status: "ready" | "submitted" | "streaming" | "error";
};

export function MessageList({ messages, status }: MessageListProps) {
  if (messages.length === 0) {
    return (
      <section className="message-list">
        <div className="empty-chat">
          <h2>Start a conversation</h2>
          <p>Ask a question or request a calculation.</p>
        </div>
      </section>
    );
  }

  return (
    <section className="message-list">
      {messages.map((message) => (
        <article key={message.id} className={`message message-${message.role}`}>
          <strong>{message.role === "user" ? "You" : "Assistant"}</strong>
          <div className="message-content">
            {message.parts.map((part, index) => {
              if (part.type === "text") {
                return <p key={`${message.id}-${index}`}>{part.text}</p>;
              }
              if (part.type === "tool-calculator") {
                return <ToolMessage key={`${message.id}-${index}`} part={part} />;
              }
              return null;
            })}
          </div>
        </article>
      ))}
      {status === "submitted" && (
        <div className="status-message">Assistant is thinking...</div>
      )}
    </section>
  );
}

function ToolMessage({ part }: { part: { state: string; input?: unknown; output?: unknown; } }) {
  if (part.state === "input-streaming" || part.state === "input-available") {
    return <div className="tool-message">Using calculator...</div>;
  }
  if (part.state === "output-available") {
    return (
      <details className="tool-message">
        <summary>Calculator result</summary>
        <pre>{JSON.stringify(part.output, null, 2)}</pre>
      </details>
    );
  }
  if (part.state === "output-error") {
    return <div className="tool-error">Calculator failed.</div>;
  }
  return null;
}
```

ဒီထိရပြီဆိုရင် Calculator tool အသုံးပြုနေတဲ့ အခြေအနေတွေကိုပါ ပြပေးနိုင်သွားပါပြီ။

`components/chat-input.tsx`

ကို ဖန်တီးပါ -

``` python
"use client";

import type { KeyboardEvent, SubmitEvent } from "react";

type ChatInputProps = {
  input: string;
  isBusy: boolean;
  onInputChange: (value: string) => void;
  onSubmit: (event: SubmitEvent<HTMLFormElement>) => void;
  onStop: () => void;
};

export function ChatInput({
  input,
  isBusy,
  onInputChange,
  onSubmit,
  onStop,
}: ChatInputProps) {
  function handleKeyDown(event: KeyboardEvent<HTMLTextAreaElement>): void {
    if (event.key === "Enter" && !event.shiftKey) {
      event.preventDefault();
      event.currentTarget.form?.requestSubmit();
    }
  }

  return (
    <form className="chat-form" onSubmit={onSubmit}>
      <textarea
        value={input}
        onChange={(event) => onInputChange(event.target.value)}
        onKeyDown={handleKeyDown}
        placeholder="Type your message..."
        rows={3}
        disabled={isBusy}
      />
      {isBusy ? (
        <button type="button" className="stop-button" onClick={onStop}>
          Stop
        </button>
      ) : (
        <button type="submit" disabled={!input.trim()}>
          Send
        </button>
      )}
    </form>
  );
}
```

`components/chat-app.tsx`

ကို အောက်ပါကုဒ်နဲ့ အစားထိုးလိုက်ပါ -

``` js
"use client";

import { useChat } from "@ai-sdk/react";
import { DefaultChatTransport, type UIMessage } from "ai";
import { type SubmitEvent, useMemo, useState } from "react";
import { useRouter } from "next/navigation";

import { ChatInput } from "@/components/chat-input";
import { ChatSidebar } from "@/components/chat-sidebar";
import { MessageList } from "@/components/message-list";
import type { ChatSummary } from "@/lib/chat-store";

type ChatAppProps = {
  chatId: string;
  initialMessages?: UIMessage[];
  initialChats: ChatSummary[];
};

export function ChatApp({
  chatId,
  initialMessages = [],
  initialChats,
}: ChatAppProps) {
  const router = useRouter();
  const [input, setInput] = useState("");

  const transport = useMemo(
    () => new DefaultChatTransport({ api: "/api/chat" }),
    [],
  );

  const {
    messages,
    sendMessage,
    status,
    error,
    stop,
    clearError,
  } = useChat({
    id: chatId,
    messages: initialMessages,
    transport,
    onFinish: () => {
      router.refresh();
    },
    onError: (chatError) => {
      console.error("Client chat error:", chatError);
    },
  });

  const isBusy = status === "submitted" || status === "streaming";

  async function handleSubmit(event: SubmitEvent<HTMLFormElement>): Promise<void> {
    event.preventDefault();
    const message = input.trim();

    if (!message || isBusy) {
      return;
    }

    clearError();
    setInput("");

    try {
      await sendMessage({ text: message });
    } catch (sendError) {
      console.error("Could not send message:", sendError);
      setInput(message); // Restore input if failed
    }
  }

  return (
    <main className="application-layout">
      <ChatSidebar activeChatId={chatId} chats={initialChats} />

      <section className="chat-panel">
        <header className="chat-header">
          <div>
            <h1>My AI Agent</h1>
            <p>Streaming agent with persistent conversations</p>
          </div>
          <span className="chat-status">{getStatusLabel(status)}</span>
        </header>

        <MessageList messages={messages} status={status} />

        {error && (
          <div className="error-message" role="alert">
            <span>{error.message || "The assistant request failed."}</span>
            <button type="button" onClick={clearError}>Dismiss</button>
          </div>
        )}

        <ChatInput
          input={input}
          isBusy={isBusy}
          onInputChange={setInput}
          onSubmit={handleSubmit}
          onStop={stop}
        />
      </section>
    </main>
  );
}

function getStatusLabel(status: "ready" | "submitted" | "streaming" | "error"): string {
  switch (status) {
    case "ready": return "Ready";
    case "submitted": return "Thinking";
    case "streaming": return "Responding";
    case "error": return "Error";
  }
}
```

`app/globals.css`

ကို အောက်ပါအတိုင်း အပြည့်အစုံ ပြောင်းလဲလိုက်ပါ -

```
* { box-sizing: border-box; }
html, body { margin: 0; min-height: 100%; }
body { overflow: hidden; background: #f4f5f7; color: #202124; font-family: Arial, Helvetica, sans-serif; }
a { color: inherit; text-decoration: none; }
button, textarea { font: inherit; }
button { cursor: pointer; }
button:disabled { cursor: not-allowed; opacity: 0.6; }

.application-layout { display: grid; grid-template-columns: 280px minmax(0, 1fr); height: 100vh; }
.chat-sidebar { display: flex; flex-direction: column; min-width: 0; padding: 16px; overflow: hidden; color: #f8fafc; background: #111827; }
.new-chat-button { display: block; margin-bottom: 16px; padding: 12px; text-align: center; background: #334155; border-radius: 8px; }
.new-chat-button:hover { background: #475569; }
.conversation-list { flex: 1; overflow-y: auto; }
.no-conversations { color: #94a3b8; font-size: 14px; }
.conversation-link { display: block; margin-bottom: 6px; padding: 11px 12px; border-radius: 8px; }
.conversation-link:hover { background: #1f2937; }
.conversation-link-active { background: #334155; }
.conversation-title { display: block; overflow: hidden; font-size: 14px; font-weight: 600; text-overflow: ellipsis; white-space: nowrap; }
.conversation-date { display: block; margin-top: 5px; color: #94a3b8; font-size: 12px; }

.chat-panel { display: flex; flex-direction: column; min-width: 0; height: 100vh; overflow: hidden; background: white; }
.chat-header { display: flex; align-items: center; justify-content: space-between; min-height: 76px; padding: 16px 24px; border-bottom: 1px solid #e5e7eb; }
.chat-header h1 { margin: 0; font-size: 20px; }
.chat-header p { margin: 5px 0 0; color: #6b7280; font-size: 13px; }
.chat-status { padding: 6px 10px; color: #475569; background: #f1f5f9; border-radius: 999px; font-size: 12px; }
.message-list { flex: 1; overflow-y: auto; padding: 28px; }
.empty-chat { max-width: 480px; margin: 120px auto; text-align: center; }
.empty-chat h2 { margin-bottom: 8px; }
.empty-chat p { color: #6b7280; }
.message { max-width: 760px; margin-bottom: 22px; padding: 14px 16px; border-radius: 12px; line-height: 1.6; }
.message strong { display: block; margin-bottom: 7px; font-size: 13px; }
.message-user { max-width: 70%; margin-left: auto; background: #dbeafe; }
.message-assistant { margin-right: auto; background: #f1f5f9; }
.message-content p { margin: 0 0 12px; white-space: pre-wrap; }
.message-content p:last-child { margin-bottom: 0; }
.status-message { color: #64748b; font-size: 14px; }
.tool-message { margin: 10px 0; padding: 10px; color: #475569; background: #e2e8f0; border-radius: 8px; font-size: 13px; }
.tool-message pre { overflow-x: auto; white-space: pre-wrap; }
.tool-error { margin-top: 10px; padding: 10px; color: #991b1b; background: #fee2e2; border-radius: 8px; }
.error-message { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin: 0 20px 12px; padding: 12px; color: #991b1b; background: #fee2e2; border-radius: 8px; }
.error-message button { padding: 5px 8px; color: #991b1b; background: transparent; border: 1px solid #fca5a5; border-radius: 6px; }
.chat-form { display: flex; gap: 12px; padding: 16px 20px 20px; border-top: 1px solid #e5e7eb; }
.chat-form textarea { flex: 1; resize: none; padding: 13px; border: 1px solid #cbd5e1; border-radius: 9px; outline: none; }
.chat-form textarea:focus { border-color: #64748b; }
.chat-form button { min-width: 90px; padding: 10px 18px; color: white; background: #1f2937; border: 0; border-radius: 8px; }
.chat-form .stop-button { background: #b91c1c; }

@media (max-width: 760px) {
  .application-layout { grid-template-columns: 1fr; }
  .chat-sidebar { display: none; }
  .message { max-width: 90%; }
  .message-list { padding: 18px; }
}
```

`.gitignore`

ဖိုင်အောက်ဆုံးမှာ ဒါလေး ထပ်ထည့်ပေးပါ -

```
.data/
```

Chat တွေက JSON ဖိုင်တွေနဲ့ သိမ်းထားတာဖြစ်လို့ Git ထဲမှာ မပါသွားဖို့ အရေးကြီးပါတယ်။

```
npm run dev
```

- Browser မှာ
`http://localhost:3000`

ကို ဖွင့်လိုက်ရင် Chat အသစ်တစ်ခုဆီကို Redirect လုပ်သွားပါမယ်။ - "My name is Lyra." လို့ ရိုက်ကြည့်ပါ။
- ပြီးရင် "What is my name?" လို့ ထပ်မေးကြည့်ပါ။ ရှေ့က စကားကို မှတ်မိနေမှာပါ။
- Page ကို Refresh လုပ်လိုက်ပါ။ Message တွေ မပျောက်သွားဘဲ ကျန်နေတာကို တွေ့ရပါမယ်။

Chat အသစ်တစ်ခု ရောက်သွားတဲ့အခါ "What is my name?" လို့ ထပ်မေးကြည့်ပါ။ အခုတစ်ခါတော့ သူ သိမှာမဟုတ်တော့ပါဘူး။ ဒါက ဘာကိုပြသလဲဆိုရင် "Memory within one conversation" နဲ့ "Memory shared across every conversation" ဆိုတာ မတူညီဘူးဆိုတာပါပဲ။

ကျွန်တော်တို့ရဲ့ AI Agent လေးကို Web App တစ်ခုအဖြစ် အောင်မြင်စွာ ပြောင်းလဲတည်ဆောက်နိုင်ခဲ့ပြီ ဖြစ်ပါတယ်။

နောက်ထပ် လေ့လာဖို့အတွက်ဆိုရင်တော့ ဒီ JSON Storage ကနေ တကယ့် Database နည်းပညာတွေနဲ့ သိမ်းဆည်းတာ၊ User authentication ထည့်တာ စတာတွေကို ဆက်လုပ်ရမှာပဲ ဖြစ်ပါတယ်။

အားလုံးပဲ အဆင်ပြေကြပါစေ။
