Skip to content

Conversation

@LielNagar
Copy link
Contributor

@LielNagar LielNagar commented Oct 27, 2025

@LielNagar LielNagar force-pushed the RDBC-959 branch 2 times, most recently from 72ce05f to 902dd9f Compare October 27, 2025 13:13
@LielNagar LielNagar changed the title RDBC-959 from base repo RDBC-959: Add AI agent feature Oct 27, 2025
@@ -0,0 +1,180 @@
package net.ravendb.client.test.client.documents.AI;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we have test that uses the AiConnectionString ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

duplicated?
#123 (comment)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

assertThat(e.getMessage())
.contains("Please provide a non-empty value for either outputSchema or sampleObject.");
}
} catch (Exception e) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why this try-catch is needed, we have the internal one, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the internal is for the test itself, and this one is required because of getDocumentStore which throws Exception. for reference:

public DocumentStore getDocumentStore() throws Exception {

private AiOperations aiOperations;

@Override
public AiOperations getAiOperations(){
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should not this be

Suggested change
public AiOperations getAiOperations(){
public AiOperations AI(){

or

Suggested change
public AiOperations getAiOperations(){
public AiOperations ai(){

similar to timeseris, bulkinsert, subscriptions, etc?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -0,0 +1,180 @@
package net.ravendb.client.test.client.documents.AI;

import net.ravendb.client.RemoteTestBase;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we have tests that uses basic Ai Agent Client Api & real ai connections, see
AiAgentClientApiBasics.cs

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, introduced AiAgentClientApiBasicTest test, in AiAgentTests.java

@@ -0,0 +1,31 @@
package net.ravendb.client.documents.operations.AI;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please check the folders structure,

for example this file in C# is in
Documents.AI.AiAnswer

you put it in:
documents.operations.AI

lets have same fodlers structure as in C#

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

@@ -0,0 +1,93 @@
package net.ravendb.client.documents.operations.AI;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be in Documents.AI folder

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

/**
* Creates or updates an AI agent configuration (with the given schema) on the database.
*/
public <TSchema> AiAgentConfigurationResult createAgent(
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

In C# we have

public AiAgentConfigurationResult CreateAgent(AiAgentConfiguration configuration)

as well

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

query.setParametersSampleObject("{\"queryName\":\"Example query parameter\"}");
agentConfiguration.setQueries(Collections.singletonList(query));

AddOrUpdateAiAgentOperation createOp = new AddOrUpdateAiAgentOperation(agentConfiguration, null);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
AddOrUpdateAiAgentOperation createOp = new AddOrUpdateAiAgentOperation(agentConfiguration, null);
AddOrUpdateAiAgentOperation createOp = new AddOrUpdateAiAgentOperation(agentConfiguration);

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

/**
* Opens an AI conversation for an agent.
*/
public AiConversation conversation(String agentId, String conversationId) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in C# AiConversationCreationOptions creationOptions is a must parameter

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

actionResponses.add(new AiAgentActionResponse(toolId, content));
}

public void setUserPrompt(String userPrompt) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

public void AddUserPrompt(params IEnumerable prompts)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}

public <TAnswer> CompletableFuture<AiAnswer<TAnswer>> stream(
String streamPropertyPath,
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we also have expression api in C#

public Task<AiAnswer<TAnswer>> StreamAsync<TAnswer>(Expression<Func<TAnswer, string>> streamPropertyPath, Func<string, Task> streamedChunksCallback, CancellationToken token = default)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}
if (this.streamPropertyPath != null) {
uriBuilder.append("&streamPropertyPath=").append(UrlUtils.escapeDataString(this.streamPropertyPath));
uriBuilder.append("&streaming=").append(UrlUtils.escapeDataString("true"));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why escape ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

*
* @param errors List to collect validation error messages.
*/
public abstract void validate(List<String> errors);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

should be validateFields, no?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Comment on lines 23 to 24
String organizationId, String projectId,
Integer dimensions, Double temperature) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

those are optional

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, made all possible combinations of ctors, because of clashing problem with ctors signatures.

/**
* Represents the version of the Vertex AI API.
*/
public enum VertexAIVersion {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move outside the class

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

this.model = model;
this.googleCredentialsJson = googleCredentialsJson;
this.location = location;
this.aiVersion = aiVersion;
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

optional

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

*/
public String getProjectId() {
try {
Pattern pattern = Pattern.compile("\"project_id\"\\s*:\\s*\"([^\"]+)\"");
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is this ? I dont see it in original code

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lets parse the json itself, without regex

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

Map<String, Object> raw = mapper.readValue(response, new TypeReference<Map<String, Object>>() {});
GetConnectionStringsResult result = mapper.readValue(response, GetConnectionStringsResult.class);

if (result.getRavenConnectionStrings() != null) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why we need this? How it was working previously?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

restored like previously.

}
}

public int getQueryEmbeddingsMaxConcurrentBatches(int globalDefault) {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move this method to tests then and pass the connection string to it, somethign like this:

	
public int getQueryEmbeddingsMaxConcurrentBatches(AiConnectionString cs, int globalDefault) {
 AbstractAiSettings provider = cs.getActiveProviderInstance();
        return provider != null && provider.getEmbeddingsMaxConcurrentBatches() != null
                ? provider.getEmbeddingsMaxConcurrentBatches()
                : globalDefault;
}

* Returns an AiOperations instance for a different database.
*/
public AiOperations forDatabase(String databaseName) {
if (this.databaseName.equalsIgnoreCase(databaseName)) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this.databaseName can be NULL , if t will be you will get an exception
please do a nullity check before comparing it

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

public final class TextPart extends ContentPart {

@JsonProperty("text")
private String text;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we should add the annotation @notblank(message = "text cannot be null or blank") if we want to make sure it won't be null or empty or just spaces

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

import org.apache.hc.core5.http.ContentType;
import java.io.IOException;

public class AddOrUpdateAiAgentCommand extends RavenCommand<AiAgentConfigurationResult> implements IRaftCommand {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the class is public
in cs private sealed class AddOrUpdateAiAgentOperationCommand
it means it is not exposed to the client

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


import java.io.IOException;

public class DeleteAiAgentCommand extends RavenCommand<AiAgentConfigurationResult> {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same issue
in the cs code it is private.
it needs to be an innerclass of DeleteAiAgentOperation

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

private final DocumentConventions conventions;
private String raftId;

public RunConversationCommand(RunConversationOperation<TAnswer> parent, DocumentConventions conventions) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same needs to be internal

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

return diffs.isEmpty() || (diffs.size() == 1 && diffs.contains(AiSettingsCompareDifferences.None));
}

public boolean usingEncryptedCommunicationChannel() {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in cs internal bool UsingEncryptedCommunicationChannel() it means that it is not exposed to someone that he is not developing on this project.
the best definition as I saw is without public or private

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#123 (comment)

@garayx what should be our approach? making both methods internal and to expose accessibility inside tests package, or keeping it public?

}
}

public int getQueryEmbeddingsMaxConcurrentBatches(int globalDefault) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same for this

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

#123 (comment)

@garayx what should be our approach? making both methods internal and to expose accessibility inside tests package, or keeping it public?

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

Successfully merging this pull request may close these issues.

3 participants