Documentação

A documentação para programadores está em inglês.

Documentação

WebKnox in the AI SDK

@webknox/ai-sdk adds one tool to the AI SDK, askWebknox. When a question has a WebKnox answer, your app shows its card, and the model adds only what the card doesn't say.

Add the tool

The tool works with AI SDK 6 and 7, on any model that can call tools.

Terminal
npm install @webknox/ai-sdk

Set WEBKNOX_API_KEY on your server, with a key from the console, and add the tool to your route:

app/api/chat/route.js
import {convertToModelMessages, isStepCount, streamText} from 'ai';
import {askWebknox} from '@webknox/ai-sdk';

const tools = {askWebknox: askWebknox()};

export async function POST(request) {
  const {messages} = await request.json();

  const result = streamText({
    model: 'openai/gpt-5-mini',
    system: 'Use askWebknox for anything it covers, even in a follow-up.',
    messages: await convertToModelMessages(messages, {tools}),
    tools,
    stopWhen: isStepCount(3),
  });

  return result.toUIMessageStreamResponse();
}

Models differ in how readily they call a tool, and some write a recipe from memory instead. The system line above tells yours to prefer WebKnox.

Show the card

The tool's result is the whole answer. In a chat built with useChat it arrives as a message part named after the tool:

app/page.jsx
message.parts.map((part) => {
  if (part.type === 'text') return <p>{part.text}</p>;
  if (part.type === 'tool-askWebknox' && part.state === 'output-available') {
    return <AnswerCard answer={part.output} onAsk={(text) => sendMessage({text})} />;
  }
});

<webknox-answer> from @webknox/elements draws every card type. It registers itself in the browser, so load it in an effect:

app/answer-card.jsx
function AnswerCard({answer, onAsk}) {
  const ref = useRef(null);

  useEffect(() => {
    import('@webknox/elements');
    const element = ref.current;
    element.answer = answer;

    const ask = (event) => onAsk(event.detail.text);
    const refine = async (event) => {
      event.preventDefault();
      const {refine, done} = event.detail;
      const shown = element.answer;
      const response = await fetch('/api/refine', {
        method: 'POST',
        body: JSON.stringify({question: shown.card.data.query ?? shown.text, locale: shown.locale, refine}),
      });
      if (response.ok) element.answer = await response.json();
      else done();
    };

    element.addEventListener('webknox-follow-up', ask);
    element.addEventListener('webknox-refine', refine);
    return () => {
      element.removeEventListener('webknox-follow-up', ask);
      element.removeEventListener('webknox-refine', refine);
    };
  }, [answer, onAsk]);

  return <webknox-answer ref={ref} />;
}

A suggested question sends webknox-follow-up with a sentence, so onAsk passes it to sendMessage as a new turn.

A change to a search card's filters, sort or results fires webknox-refine. Don't send that through the model, which may answer in text and leave the card as it was. Answer it from your server with refineAnswer, and the card updates in place:

app/api/refine/route.js
import {refineAnswer} from '@webknox/ai-sdk';

export async function POST(request) {
  const {question, locale, refine} = await request.json();
  return Response.json(await refineAnswer({question, locale, refine}));
}

Add your own auth and rate limits to that route before it goes public, since it spends your key. The Next.js chat is the complete app.

What the model reads

The UI gets the whole answer, and the model gets only its text and sources, with a note that the user already sees the card. A large card stays out of its context that way, and it doesn't read the card back to the user.

Pass the same tools object to convertToModelMessages, as above, so earlier turns are sent the same short way.

Options

The model passes one self-contained question. Everything else is set once, on the tool:

route.js
askWebknox({
  locale: 'de',
  context: {timezone: 'Europe/Berlin', units: 'metric'},
  accept: ['weather.forecast', 'nutrition.facts'],
});
OptionMeaning
localeen (default), de, fr, es, pt or it
contextTime zone, current time, location and units, for “today” and “here”
acceptThe card types your app can show; omit it to accept all
renderHosted embeds and images, on Pro and Scale
apiKeyDefaults to WEBKNOX_API_KEY
baseUrl, fetchFor proxies and tests

No answer and errors

A question WebKnox can't answer returns matched: false. The model is told so and answers it itself, and the call isn't billed.

A refused request throws a WebKnoxError with status, code and retryAfterS, from the API's errors. The AI SDK passes it to the model as a failed tool call.