Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Request for feedback #30

Closed
TomerAberbach opened this issue Dec 20, 2024 · 17 comments
Closed

Request for feedback #30

TomerAberbach opened this issue Dec 20, 2024 · 17 comments

Comments

@TomerAberbach
Copy link
Collaborator

Hey everyone!

This SDK is in alpha, and we are actively iterating on its design and making improvements. We would love to hear your thoughts and feedback!

As you try the new SDK, please post your thoughts and feedback on this issue. We'll be actively monitoring this thread.

Thanks in advance! We hope to make a Java SDK you'll love using.

@sgflt
Copy link

sgflt commented Jan 4, 2025

Batch API

I am stuck at retrieving batch results.

  public MessageBatch getResults(final String batchId) {
    return this.client.messages()
        .batches()
        .retrieve(MessageBatchRetrieveParams.builder().messageBatchId(batchId).build());
  }

MessageBatch contains resultsUrl, but it seems there is a part of API missing.

I would expect a stream/list of jsonl records to be available in similar way as in python

client.messages.batches.results(
    "msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d",
):

doc

API model
I feel kind of confused by multiple implementations with the same meaning and structure. F. e.:

var SYSTEM_PROMPT = "system prompt as string";
MessageBatchCreateParams.Request.Params.builder()                            
      .system(MessageBatchCreateParams.Request.Params.System.ofString(SYSTEM_PROMPT))
      // there is no string overload


MessageCreateParams.builder()
    .system(MessageCreateParams.System.ofString(SYSTEM_PROMPT))
    // or
    .system(SYSTEM_PROMPT)

@katoquro
Copy link

katoquro commented Jan 5, 2025

Hello,

Is it possible to have a separate library containing only the model types, without additional dependencies? We do not use OkHttp and have our own client, so we would like to have the "official" types and validators available separately. However, having a ready-made client as a separate artifact is beneficial for building smaller projects, like a Lambda function, where minimizing dependencies is important.
Thank you.

@TomerAberbach
Copy link
Collaborator Author

Batch API

I am stuck at retrieving batch results.

public MessageBatch getResults(final String batchId) {
return this.client.messages()
.batches()
.retrieve(MessageBatchRetrieveParams.builder().messageBatchId(batchId).build());
}
MessageBatch contains resultsUrl, but it seems there is a part of API missing.

I would expect a stream/list of jsonl records to be available in similar way as in python

client.messages.batches.results(
"msgbatch_01HkcTjaV5uDC8jWR4ZsDV8d",
):
doc

Yeah, support for this API is incomplete. JSONL support isn't built out properly yet. Filed an issue here for you to follow: #42

API model I feel kind of confused by multiple implementations with the same meaning and structure. F. e.:

var SYSTEM_PROMPT = "system prompt as string";
MessageBatchCreateParams.Request.Params.builder()
.system(MessageBatchCreateParams.Request.Params.System.ofString(SYSTEM_PROMPT))
// there is no string overload

MessageCreateParams.builder()
.system(MessageCreateParams.System.ofString(SYSTEM_PROMPT))
// or
.system(SYSTEM_PROMPT)

Yup, this is a known issue. We will soon have these convenience overloads recursively :)

@TomerAberbach
Copy link
Collaborator Author

Hello,

Is it possible to have a separate library containing only the model types, without additional dependencies? We do not use OkHttp and have our own client, so we would like to have the "official" types and validators available separately. However, having a ready-made client as a separate artifact is beneficial for building smaller projects, like a Lambda function, where minimizing dependencies is important. Thank you.

So, the SDK is already split into multiple packages:

  • anthropic-java-client-okhttp (depends on OkHttp)
  • anthropic-java-core (doesn't depend on OkHttp)
  • anthropic-java (depends on both anthropic-java-client-okhttp and anthropic-java-core)

You should be able to depend on anthropic-java-core only and bring your own HTTP client by implementing HttpClient using your client, and then using it to construct the Anthropic clients (see OkHttp examples).

Does that answer your question @katoquro?

@katoquro
Copy link

katoquro commented Jan 9, 2025

Thanks for response, @TomerAberbach.

I saw that module. Isn't org.apache.httpcomponents.client5:httpclient5 an http client?
It's listed here

implementation("org.apache.httpcomponents.client5:httpclient5:5.3.1")

@TomerAberbach
Copy link
Collaborator Author

@katoquro apologies for the delayed reply.

I'll need to look into why we depend on that, but we definitely don't use that as our HTTP client in the core module. Maybe we depend on it for something else, or maybe it's a bug. I'll get back to you on that

@Mubai0628
Copy link

Sorry to bother you, why do I always get 403 when I try to access Claude through the api in Java? But I succeed directly in python? Why does Java show that ANTHROPIC_AUTH_TOKEN is still needed?

@sgflt
Copy link

sgflt commented Jan 18, 2025

I am trying to implement custom tool, but I find it difficult to find a correct way, how to build shema.

            // TODO custom tool
            .addTool(
                Tool.builder().inputSchema(
                    Tool.InputSchema.builder()
                        .putAdditionalProperty("my_tool_property", /* ??? */)
                        .build()
                )
            )

I would expect direct input:

             .addTool(
                 """
                 {
        "name": "get_weather",
        "description": "Get the current weather in a given location",
        "input_schema": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "The city and state, e.g. San Francisco, CA"
                },
                "unit": {
                    "type": "string",
                    "enum": ["celsius", "fahrenheit"],
                    "description": "The unit of temperature, either \"celsius\" or \"fahrenheit\""
                }
            },
            "required": ["location"]
        }
    
                 """
                )
            )

or builder with expectable factory methods:

            .addTool(
                Tool.builder().name("get_weather")
                .inputSchema(
                    Tool.InputSchema.builder()
                        .addRequiredString("location", "The city and state, e.g. San Francisco, CA")
                        .addEnum("unit", "The unit of temperature, either \"celsius\" or \"fahrenheit\"", Temperature.values())
                        .build()
                ).build()
            )

or

            .addTool(
                Tool.builder().name("get_weather")
                .inputSchema(
                    Tool.InputSchema.builder()
                        .addRequiredProperty(ToolProperty.string("location", "The city and state, e.g. San Francisco, CA"))
                        .addProperty(ToolProperty.enum("unit", "The unit of temperature, either \"celsius\" or \"fahrenheit\"", Temperature.values()))
                        .build()
                ).build()
            )

or

            .addTool(
                Tool.builder().name("get_weather")
                .inputSchema(
                    Tool.InputSchema.builder()
                        .addProperty(ToolProperty.string("location", "The city and state, e.g. San Francisco, CA", ToolProperty.REQUIRED))
                        .addProperty(ToolProperty.enum("unit", "The unit of temperature, either \"celsius\" or \"fahrenheit\"", Temperature.values()))
                        .build()
                ).build()
            )

@dengpan1
Copy link

Is there a temporary workaround until the SDK doesn't officially support getting results?

@TomerAberbach
Copy link
Collaborator Author

Thanks for response, @TomerAberbach.

I saw that module. Isn't org.apache.httpcomponents.client5:httpclient5 an http client? It's listed here

anthropic-sdk-java/anthropic-java-core/build.gradle.kts

Line 16 in 04dddd5

implementation("org.apache.httpcomponents.client5:httpclient5:5.3.1")

@katoquro looks like we use it for org.apache.hc.client5.http.entity.mime.MultipartEntityBuilder

@TomerAberbach
Copy link
Collaborator Author

Sorry to bother you, why do I always get 403 when I try to access Claude through the api in Java? But I succeed directly in python? Why does Java show that ANTHROPIC_AUTH_TOKEN is still needed?

@Mubai0628

Without more information I don't know why it's not working. Please file a new issue with more details if it's still not working

@TomerAberbach
Copy link
Collaborator Author

I am trying to implement custom tool, but I find it difficult to find a correct way, how to build shema.

        // TODO custom tool
        .addTool(
            Tool.builder().inputSchema(
                Tool.InputSchema.builder()
                    .putAdditionalProperty("my_tool_property", /* ??? */)
                    .build()
            )
        )

I would expect direct input:

         .addTool(
             """
             {
    "name": "get_weather",
    "description": "Get the current weather in a given location",
    "input_schema": {
        "type": "object",
        "properties": {
            "location": {
                "type": "string",
                "description": "The city and state, e.g. San Francisco, CA"
            },
            "unit": {
                "type": "string",
                "enum": ["celsius", "fahrenheit"],
                "description": "The unit of temperature, either \"celsius\" or \"fahrenheit\""
            }
        },
        "required": ["location"]
    }

             """
            )
        )

or builder with expectable factory methods:

        .addTool(
            Tool.builder().name("get_weather")
            .inputSchema(
                Tool.InputSchema.builder()
                    .addRequiredString("location", "The city and state, e.g. San Francisco, CA")
                    .addEnum("unit", "The unit of temperature, either \"celsius\" or \"fahrenheit\"", Temperature.values())
                    .build()
            ).build()
        )

or

        .addTool(
            Tool.builder().name("get_weather")
            .inputSchema(
                Tool.InputSchema.builder()
                    .addRequiredProperty(ToolProperty.string("location", "The city and state, e.g. San Francisco, CA"))
                    .addProperty(ToolProperty.enum("unit", "The unit of temperature, either \"celsius\" or \"fahrenheit\"", Temperature.values()))
                    .build()
            ).build()
        )

or

        .addTool(
            Tool.builder().name("get_weather")
            .inputSchema(
                Tool.InputSchema.builder()
                    .addProperty(ToolProperty.string("location", "The city and state, e.g. San Francisco, CA", ToolProperty.REQUIRED))
                    .addProperty(ToolProperty.enum("unit", "The unit of temperature, either \"celsius\" or \"fahrenheit\"", Temperature.values()))
                    .build()
            ).build()
        )

@sgflt you can use JsonValue like so:

Tool.InputSchema.builder()
  .type(Tool.InputSchema.Type.OBJECT) // Note: we are working on a change that will likely let you omit this
  .properties(JsonValue.from(
    Map.of(
      "location", Map.of("type", "string", "description", "..."),
      "unit", Map.of(
        "type", "string",
        "enum", List.of("celsius", "fahrenheit"),
        "description", "..."
      )
    )
  ))

@TomerAberbach
Copy link
Collaborator Author

Is there a temporary workaround until the SDK doesn't officially support getting results?

@dengpan1 no, there's no workaround. But I suspect we'll have support released this week

@sgflt
Copy link

sgflt commented Jan 22, 2025

@sgflt you can use JsonValue like so:

Tool.InputSchema.builder()
.type(Tool.InputSchema.Type.OBJECT) // Note: we are working on a change that will likely let you omit this
.properties(JsonValue.from(
Map.of(
"location", Map.of("type", "string", "description", "..."),
"unit", Map.of(
"type", "string",
"enum", List.of("celsius", "fahrenheit"),
"description", "..."
)
)
))

OK, thanks for pointing that possibility. It seems to be sufficient for my use case.

But, I am new to AI APIs and for me they seem to be very low level.
I think I am doing things in a wrong way or there may be API improved to get less steep learning curve. I'm just staring at the code, trying to figure out how to parse the responses.

For example:

 final var response = this.client.messages().create(
        messageBuilder
            .addMessage(
                MessageParam.builder()
                    .role(MessageParam.Role.USER)
                    .content(MessageParam.Content.ofString(body))
                    .build()
            ).build()
    );

    final var stopReason =
        response.stopReason().map(Message.StopReason::value).orElse(Message.StopReason.Value._UNKNOWN);
    if (stopReason == Message.StopReason.Value.TOOL_USE) {
      response.content().stream()
          .filter(ContentBlock::isToolUseBlock)
          .findFirst() // assuming there is only one possible tool use block in turn response
          .map(ContentBlock::asToolUseBlock)
          .ifPresent(toolUseBlock -> {
            messageBuilder.addMessage(response.toParam());
            // TODO tool call
            messageBuilder.addMessage(
                MessageParam.builder()
                    .role(MessageParam.Role.USER)
                    .content(MessageParam.Content.ofContentBlockParams(
                        List.of(
                            ContentBlockParam.ofToolResultBlockParam(
                                ToolResultBlockParam.builder()
                                    .toolUseId(toolUseBlock.id())
                                    .content(ContentBlock.ofTextBlock(toolResponse))
                                    .build()
                            )
                        )
                    ))
                    .build()
            );
          });
    }

    return response;

I would wish kind of conversational object that does all this automagically:

    final var conversation = new AnthropicConversation();
    conversation.system("system prompt");
    conversation.addTool(toolDefinition, toolHandler);
    conversation.addTool(...);
    conversation.addTool(...);
    conversation.addTool(...);

    conversation.addMessage(
          MessageParam.builder()
              .role(MessageParam.Role.USER)
              .content(MessageParam.Content.ofString(body))
              .build()
    );

    // send initial prompt
    // invoke tools if AI asked tool_use
    // handles all turns internally, maintains USER/ASSISTANT messages
    // return final response as string
    String finalResponse = conversation.talkToCentralBrainOfMankind();

@TomerAberbach
Copy link
Collaborator Author

@dengpan1 the results endpoints are now shipped as of v0.1.0-alpha.9

@TomerAberbach
Copy link
Collaborator Author

@sgflt just an FYI that I've added some examples that you may find helpful: https://github.com/anthropics/anthropic-sdk-java/tree/main/anthropic-java-example/src/main/java/com/anthropic/example

Let me know if you're still finding the SDK hard to use after looking at them

(also make sure to update to the latest version before looking)

@TomerAberbach
Copy link
Collaborator Author

This issue has gotten a little out of hand due to the lack of threading, so we're switching to the Discussions feature.

If you have additional feedback, please file a new issue or comment here: #95

@TomerAberbach TomerAberbach closed this as not planned Won't fix, can't repro, duplicate, stale Jan 29, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

5 participants