anthropics / anthropic-sdk-typescript

Access to Anthropic's safety-first language model APIs
https://www.npmjs.com/package/@anthropic-ai/sdk
MIT License
505 stars 49 forks source link

[Types] Error: Property 'text' does not exist on type 'ContentBlock'. #432

Open davidstackio opened 1 month ago

davidstackio commented 1 month ago

After updating to v0.22.0 from v0.20.9, I'm getting a type error on the last line of this block (text = msg.content[0].text;):

const msg = await anthropic.messages.create({
      model: "claude-3-opus-20240229",
      max_tokens: maxOutputTokens,
      temperature: 0.9,
      messages: [
        {
          role: "user",
          content: [
            {
              type: "text",
              text: prompt,
            },
          ],
        },
      ],
    });
    text = msg.content[0].text;

The type error is:

Property 'text' does not exist on type 'ContentBlock'.
  Property 'text' does not exist on type 'ToolUseBlock'.ts(2339)

When logging the message content, it does not appear to have changed, so I'm guessing this is just a type bug that needs fixed. My code works as expected when adding the // @ts-ignore comment above the problem line, which is my current workaround.

olu-an commented 1 month ago

cc @rattrayalex to see if it's an SDK issue

bcherny commented 2 weeks ago

It seems like this is an intentional change introduced in https://github.com/anthropics/anthropic-sdk-typescript/pull/429.

Prior to that PR, response messages were always of type text (ie. content generated by Claude). After the PR, responses can be either text(as before) or tool_use, meaning Claude invoked an external tool.

To fix the TypeScript error, you'll want to refine Claude's response, to explicitly check which type of response it is. It is no longer safe to assume the response is always text:

const response = msg.content[0];
switch (response.type) {
  case 'text':
    const text = response.text;
    // ...
  case 'tool_use':
    // ...
}

You can also use if or any other conditional check to refine the response.

Alternatively, if you know the response will always be content (ie. you did not configure a tool), you can use a type assertion. Though, note this is not as typesafe as an explicit if:

const text = (msg.content[0] as Anthropic.TextBlock).text;

See https://docs.anthropic.com/en/docs/build-with-claude/tool-use#chain-of-thought


For Anthropic folks -- there's a minor improvement you might consider to make the SDK a bit easier to use: add an overloaded type signature to create, so that it returns content of type ContentBlock if tool is configured, and otherwise returns content of type TextBlock. That would make it so the PR above doesn't break builds for folks that don't use tools.

rattrayalex commented 2 weeks ago

Hey, sorry for the delay at taking a look here. @bcherny is correct that this is intentional, but I also quite like the overload idea. No guarantees we'll be able to get to it soon, but I'll shop it around.

rattrayalex commented 2 weeks ago

I'll leave this issue open, even though it is indeed Working As Intended, because I'd really like to improve the developer experience here.

jonnicholson94 commented 1 week ago

Just to comment, I had this problem this morning - didn't find the docs too clear, and a bit hacky (in my opinion) to have to explicitly declare the type like that when it'll always be 'text' for my example. A solution would be great!