HextaAI

Tüm HextaUI yapay zeka bloklarından oluşturulmuş eksiksiz bir yapay zeka sohbet uygulaması. Kenar çubuğunda sohbetler, kaynaklı düşünme, fark ve onaylı araç çağrıları, ajan çalışmadan önce gözden geçirdiğiniz bir plan, akışla gelen Markdown ve kod, ve sessiz ses modu; hepsi AI SDK mesaj parçalarıyla yönetilir.

HextaAI, tüm HextaUI yapay zeka bloklarından yapılmış eksiksiz bir yapay zeka sohbet uygulamasıdır. Chat Sidebar geçmişi, Chat Thread konuşmayı tutar, Prompt Input yazma alanıdır ve her yanıt parçalarından çizilir: akıl yürütme için Thinking, ajanın yaptıkları için Tool Calls, planı için Agent Todos, fark ve terminaller için Code Block, yanıt için Streaming Markdown. Artifact, modelin yaptığı web sayfalarını, belgeleri ve dosyaları sohbetin yanındaki bir panelde sürümleriyle açar ve küçük değişiklikler dosyayı yeniden yazmak yerine açık dosyaya yazılır. Voice Mode yazma alanından açılır ve kapattığınızda konuşmayı sohbete bırakır.

Çekirdek HextaMessageParts'tır. AI SDK'nın useChat'inin döndürdüğü mesaj parçalarını aynen alır, komşu akıl yürütme ve araç çağrılarını gruplar, yanıtın en üstünde tek bir canlı plan tutar ve metni, modelin bulduğu kaynaklara bağlı alıntılarla render eder. Okumalar, düzenlemeler, yazmalar, aramalar, web getirmeleri ve kabuk komutlarının her biri uygun bir görünüm alır ve kendi araçlarınız için renderer ekleyebilirsiniz.

Demo, gerçekçi parçaları çevrimdışı akıtan küçük bir motor olan ScriptedChats üzerinde çalışır. Bir öneri deneyin: bir fiyatlandırma sayfası yaptırın ve sohbetin yanında açılışını izleyin, bir soruyu araştırtın ve kaynakların geldiğini görün, bir özellik isteyin ve çalışmadan önce planı gözden geçirin, başarısız olup düzeltilen bir test çalıştırmasını onaylayın ya da kod ve tablolarla bir açıklama isteyin. Yanıtlar arka planda akmaya devam ederken durdurun, yeniden oluşturun, düzenleyin ve sohbetler arasında geçin; ardından ses modunu açıp konuşun.

  1. Pro registry'yi components.json dosyasına ekleyin

    components.json
    {
      "registries": {
        "@hextaui-pro": {
          "url": "https://hextaui.com/r/pro/{name}.json",
          "headers": {
            "Authorization": "Bearer ${HEXTAUI_PRO_TOKEN}"
          }
        }
      }
    }
  2. Token'ınızı ekleyin

    Hesap sayfanızda bir token oluşturun ve .env.local dosyasına HEXTAUI_PRO_TOKEN olarak ekleyin.

  3. Bloğu ekleyin

    pnpm dlx shadcn@latest add @hextaui-pro/hexta-ai

AI SDK ile

useChat mesajlarını doğrudan HextaMessageParts'a iletin. Araç onayları ve plan gözden geçirmeleri addToolApprovalResponse ile yanıtlanır ve düzenlenmiş bir plan modele onay gerekçesi olarak geri gider.

"use client"

import { useChat } from "@ai-sdk/react"
import { lastAssistantMessageIsCompleteWithApprovalResponses } from "ai"

import { ChatAssistantMessage, ChatThread, ChatUserMessage } from "../chat-thread/chat-thread"
import { PromptInput, PromptInputActions, PromptInputBody, PromptInputSubmit, PromptInputTextarea } from "../prompt-input/prompt-input"
import { HextaMessageParts } from "@/components/blocks/hexta-ai/message-parts"
import { textOf, type HextaMessage, type ToolPart } from "@/components/blocks/hexta-ai/parts"

export function Assistant() {
  const { messages, status, sendMessage, stop, regenerate, addToolApprovalResponse } = useChat({
    sendAutomaticallyWhen: lastAssistantMessageIsCompleteWithApprovalResponses,
  })
  const busy = status === "submitted" || status === "streaming"
  const chat = messages as HextaMessage[]

  const approvalId = (toolCallId: string) =>
    chat
      .flatMap((message) => message.parts)
      .find((part): part is ToolPart => "toolCallId" in part && part.toolCallId === toolCallId)
      ?.approval?.id

  return (
    <ChatThread
      busy={busy}
      composer={
        <PromptInput status={status} onStop={stop} onSubmit={({ text }) => sendMessage({ text })}>
          <PromptInputBody>
            <PromptInputTextarea />
            <PromptInputActions>
              <PromptInputSubmit />
            </PromptInputActions>
          </PromptInputBody>
        </PromptInput>
      }
    >
      {chat.map((message, index) => {
        const last = index === chat.length - 1
        if (message.role === "user") {
          return <ChatUserMessage key={message.id} id={message.id} text={textOf(message)} />
        }
        return (
          <ChatAssistantMessage
            key={message.id}
            id={message.id}
            text={textOf(message)}
            streaming={last && busy}
            last={last}
            onRetry={last ? () => regenerate() : undefined}
          >
            <HextaMessageParts
              parts={message.parts}
              streaming={last && busy}
              onApproval={(toolCallId, { approved, reason }) => {
                const id = approvalId(toolCallId)
                if (id) addToolApprovalResponse({ id, approved, reason })
              }}
              onPlanApprove={(toolCallId, todos) => {
                const id = approvalId(toolCallId)
                if (!id) return
                addToolApprovalResponse({
                  id,
                  approved: true,
                  reason: `Run this plan:\n${todos.map((todo, step) => `${step + 1}. ${todo.content}`).join("\n")}`,
                })
              }}
              onPlanCancel={(toolCallId) => {
                const id = approvalId(toolCallId)
                if (id) addToolApprovalResponse({ id, approved: false })
              }}
            />
          </ChatAssistantMessage>
        )
      })}
    </ChatThread>
  )
}

Kendi araçlarınız

Bir araç adını; türünü, konusunu ve içeriğini döndüren bir renderer'a eşleyin. Eşlemediğiniz araçlar yerleşik görünümlere ya da bir JSON görünümüne düşer.

"use client"

import { CodeBlock } from "../code-block/code-block"
import { HextaMessageParts, type ToolRenderer } from "@/components/blocks/hexta-ai/message-parts"
import type { MessagePart } from "@/components/blocks/hexta-ai/parts"

const tools: Record<string, ToolRenderer> = {
  create_chart: (part) => {
    const input = part.input as { title?: string; spec?: string } | undefined
    return {
      kind: "write",
      subject: input?.title ?? "Chart",
      content: input?.spec ? <CodeBlock code={input.spec} language="json" filename="chart.json" /> : undefined,
    }
  },
}

export function Reply({ parts, streaming }: { parts: MessagePart[]; streaming: boolean }) {
  return <HextaMessageParts parts={parts} streaming={streaming} tools={tools} />
}

Betikli demolar

ScriptedChats, gerçekçi hız, onaylar ve durdurma ile bir betikten AI SDK biçimli parçaları akıtır. Ürün turları, onboarding ve çevrimdışı demolar için kullanın.

"use client"

import * as React from "react"

import { HextaMessageParts } from "@/components/blocks/hexta-ai/message-parts"
import { textOf } from "@/components/blocks/hexta-ai/parts"
import { ScriptedChats, useScriptedChat, type Script } from "@/components/blocks/hexta-ai/scripted-chat"

const onboarding: Script = async (run) => {
  await run.think("**Picking a first step**\nShow them the one thing that makes the product click.")
  const search = await run.tool("web_search", { query: "best onboarding checklists" })
  await run.pause(600)
  search.finish({ results: 4 })
  await run.say("Start with **one task they can finish in a minute**, then show what it unlocked.")
}

export function ProductTour() {
  const [store] = React.useState(() => new ScriptedChats(() => onboarding))
  const { messages, status } = useScriptedChat(store, "tour")
  const busy = status === "submitted" || status === "streaming"

  return (
    <div className="flex flex-col gap-4">
      <button onClick={() => void store.send("tour", "How should onboarding start?")}>Play</button>
      {messages.map((message, index) =>
        message.role === "user" ? (
          <p key={message.id}>{textOf(message)}</p>
        ) : (
          <HextaMessageParts
            key={message.id}
            parts={message.parts}
            streaming={busy && index === messages.length - 1}
            reasoningDurations={message.metadata?.thoughts}
            onApproval={(id, approval) => store.decide(id, approval)}
          />
        )
      )}
    </div>
  )
}

Yapı

Dıştan içe doğru birleştirdiğiniz parçalar.

ParçaAçıklama
HextaMessagePartsTek bir asistan mesajını parçalarından sırayla render eder: akıl yürütme, plan, araç çağrıları ve Markdown.
defaultToolsread_file, list_files, grep, web_search, fetch, edit_file, write_file ve bash için yerleşik renderer'lar.
ScriptedChats, useScriptedChatBetiklerden AI SDK biçimli parçaları akıtan, durdurma, yeniden oluşturma, düzenleme, onay ve plan gözden geçirme destekli çevrimdışı bir motor.
textOf, sourcesOf, toBlocksBir mesajdan metin ve kaynakları okuyan ve parçalarını renderer'ın yaptığı gibi gruplayan yardımcılar.
HextaAIDemoTam uygulama: ScriptedChats'a bağlı Chat Sidebar, Chat Thread, Prompt Input ve Voice Mode.
PropTürVarsayılan
partsMesajın parçaları: useChat'in döndürdüğü şekliyle text, reasoning, source-url, file ve araç parçaları.
MessagePart[]–
streamingBu mesajın hâlâ gelmekte olup olmadığı. Son akıl yürütme veya metin bloğu canlanır ve çalışan araçlar canlı kalır.
booleanfalse
stoppedÇalışan araçları iptal edilmiş olarak işaretler ve bekleyen bir plan gözden geçirmeyi gizler.
booleanfalse
toolsAraç adına göre renderer'lar. Her biri kind, subject, title, meta, content, exitCode veya error döndürür ve yerleşik görünümü geçersiz kılar.
Record<string, ToolRenderer>{}
onApprovalBiri onay bekleyen bir araç çağrısına izin verdiğinde veya reddettiğinde çağrılır.
(toolCallId, { approved, reason?, always? }) => void–
onPlanApproveBiri gözden geçirmeden planı çalıştırdığında, düzenlenmiş planla çağrılır.
(toolCallId, todos) => void–
onPlanCancelPlan gözden geçirme iptal edildiğinde çağrılır.
(toolCallId) => void–
reasoningDurationsHer akıl yürütme bloğunda geçen saniyeler; böylece “Thought for” yeniden yüklemeden sonra da doğru kalır.
number[]–
PropTürVarsayılan
new ScriptedChats(respond)Her mesajı yanıtlayan betiği seçer.
(prompt, files) => Script–
send(chatId, text, files?, options?)Bir kullanıcı mesajı ekler ve yanıtı akıtır. options: model, thinking ve tüm beklemeyi atlayan instant.
Promise<void>–
stop, regenerate, edit, append, removeYanıtı durdurun, sonuncuyu yineleyin, düzenlenmiş bir mesajdan yeniden gönderin, tamamlanmış mesajlar ekleyin ya da bir sohbeti bırakın.
methods–
decide(toolCallId, decision)Bekleyen bir onaya ya da plan gözden geçirmeye yanıt verir. Düzenlenmiş bir planı çalıştırmak için todos iletin.
void–
useScriptedChat(store, chatId)Bir bileşeni tek bir sohbete abone eder.
{ messages, status, pinned }–
PropTürVarsayılan
think(text)Bir akıl yürütme parçasını akıtır. Thinking kapalıysa atlanır.
Promise<void>–
say(markdown)Gerçek bir model gibi, düzensiz parçalarla bir metin parçasını akıtır.
Promise<void>–
tool(name, input, { stream? })Bir araç çağrısı ekler, isteğe bağlı olarak tek bir girdi alanını akıtır. Tanıtıcı onaylayabilir, ilerletebilir, bitirebilir veya başarısız kılabilir.
Promise<ToolHandle>–
plan(todos)Gözden geçirme için bir plan gösterir ve bekler. Düzenlenmiş planı döndürür, iptal edilirse null.
Promise<Todo[] | null>–
todos(todos), source(page), pause(ms)Planı güncelleyin, bir kaynağı alıntılayın ya da bekleyin.
methods–
TuşAction
⌘KSohbetlerinizde arar.
⌘⇧OYeni bir sohbet başlatır.
⌘BKenar çubuğunu simgelere daraltır ya da telefonlarda tam ekran açar.
EnterMesajı gönderir. Shift+Enter yeni satır ekler.
⌘↵Gözden geçirilirken planı çalıştırır ya da bekleyen bir araç çağrısına izin verir.
⌘⌫Bekleyen araç çağrısını reddeder.
EscYanıtı durdurur, bir menüyü kapatır ya da ses modunu bitirir.
  • Her parça, onu çizen bloğun erişilebilirliğini korur: thread yeni yanıtları polite olarak duyurur, araç çağrıları ve planlar durumlarını metin olarak sunar, alıntılar kaynaklarını adlandırır ve onaylar klavye kısayollu gerçek düğmelerdir.
  • Önemli hiçbir şey yalnızca hareketle ya da renkle gösterilmez. Spinner'ların “Replying” gibi etiketleri vardır, arka planda biten sohbetler “Unread reply” etiketi alır ve farklar eklenen ve kaldırılan satırları renkle birlikte işaretlerle de belirtir.
  • Akış, shader ve sheet animasyonlarının hepsi azaltılmış hareketi dikkate alır. Ses modu yanıtları sesli okumadan çalışır, altyazı gösterir ve mikrofon yoksa örnek bir konuşma oynatır.

Şunlarla geliştirildi

HextaAI bloğunun yapıldığı ücretsiz HextaUI bileşenleri. Her biri tek başına yüklenir.

Kod

5 dosya, components/blocks/hexta-ai konumuna eklendi.