Skip to content

Commit 3adacf1

Browse files
committed
RDBC-926 Refactor AI agent operations to simplify request creation, improve serialization, and enhance response parsing.
1 parent 3286b58 commit 3adacf1

File tree

4 files changed

+43
-110
lines changed

4 files changed

+43
-110
lines changed

src/Documents/Operations/AI/Agents/AddOrUpdateAiAgentOperation.ts

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,6 @@ import { ServerNode } from "../../../../Http/ServerNode.js";
99
import { HttpRequestParameters } from "../../../../Primitives/Http.js";
1010
import { RaftIdGenerator } from "../../../../Utility/RaftIdGenerator.js";
1111
import { throwError } from "../../../../Exceptions/index.js";
12-
import { ObjectUtil } from "../../../../Utility/ObjectUtil.js";
13-
import { JsonSerializer } from "../../../../Mapping/Json/Serializer.js";
1412
import { HeadersBuilder } from "../../../../Utility/HttpUtil.js";
1513

1614
function hasNoSampleObjectOrSchema(configuration: AiAgentConfiguration) {
@@ -54,7 +52,7 @@ class AddOrUpdateAiAgentCommand extends RavenCommand<AiAgentConfigurationResult>
5452
public constructor(configuration: AiAgentConfiguration, sampleSchema: any, conventions: DocumentConventions) {
5553
super();
5654
if (hasNoSampleObjectOrSchema(configuration)) {
57-
throw new Error("Please provide a non-empty value for either outputSchema or sampleObject.");
55+
throwError("InvalidArgumentException", "Please provide a non-empty value for either outputSchema or sampleObject.");
5856
}
5957
this._configuration = configuration;
6058
this._sampleSchema = sampleSchema;
@@ -71,29 +69,18 @@ class AddOrUpdateAiAgentCommand extends RavenCommand<AiAgentConfigurationResult>
7169

7270
createRequest(node: ServerNode): HttpRequestParameters {
7371
const uri = node.url + "/databases/" + node.database + "/admin/ai/agent";
74-
const configToSend = {...this._configuration};
7572

76-
if (!configToSend.sampleObject && this._sampleSchema) {
77-
configToSend.sampleObject = this._sampleSchema;
73+
if (!this._configuration && this._sampleSchema) {
74+
this._configuration.sampleObject = this._sampleSchema;
7875
}
7976

80-
const bodyToSerialize = {
81-
...configToSend,
82-
parameters: configToSend.parameters ? Array.from(configToSend.parameters) : []
83-
};
84-
85-
const bodyJson = ObjectUtil.transformObjectKeys(bodyToSerialize, {
86-
defaultTransform: ObjectUtil.pascal
87-
});
88-
89-
const body = JsonSerializer.getDefault().serialize(bodyJson);
77+
const body = this._serializer.serialize(this._configuration);
9078

9179
const headers = HeadersBuilder
9280
.create()
9381
.typeAppJson()
9482
.build();
9583

96-
console.log(uri);
9784
return {
9885
method: "PUT",
9986
uri,

src/Documents/Operations/AI/Agents/DeleteAiAgentOperation.ts

Lines changed: 1 addition & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -55,21 +55,6 @@ class DeleteAiAgentCommand extends RavenCommand<AiAgentConfigurationResult> {
5555
this._throwInvalidResponse();
5656
}
5757

58-
let body: string = "";
59-
const result = await this._defaultPipeline()
60-
.collectBody(b => body = b)
61-
.process(bodyStream);
62-
63-
this.result = result as AiAgentConfigurationResult;
64-
return body;
65-
// let body = "";
66-
// const data = await this._defaultPipeline<any>(_ => body = _).process(bodyStream);
67-
//
68-
// this.result = {
69-
// identifier: data?.Identifier ?? data?.identifier,
70-
// raftCommandIndex: data?.RaftCommandIndex ?? data?.raftCommandIndex
71-
// } as AiAgentConfigurationResult;
72-
//
73-
// return body;
58+
return this._parseResponseDefaultAsync(bodyStream)
7459
}
7560
}

src/Documents/Operations/AI/Agents/GetAiAgentsOperation.ts

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -32,32 +32,28 @@ class GetAiAgentsCommand extends RavenCommand<GetAiAgentsResponse> {
3232
this._conventions = conventions;
3333
}
3434

35+
get isReadRequest(): boolean {
36+
return true;
37+
}
38+
3539
createRequest(node: ServerNode): HttpRequestParameters {
3640
let uri = `${node.url}/databases/${node.database}/admin/ai/agent`;
41+
3742
if (this._agentId) {
3843
uri += `?agentId=${encodeURIComponent(this._agentId)}`;
3944
}
45+
4046
return {
4147
method: "GET",
4248
uri
4349
};
4450
}
4551

46-
get isReadRequest(): boolean {
47-
return true;
48-
}
49-
5052
async setResponseAsync(bodyStream: Stream, fromCache: boolean): Promise<string> {
5153
if (!bodyStream) {
5254
this._throwInvalidResponse();
5355
}
5456

55-
let body: string = "";
56-
const result = await this._defaultPipeline()
57-
.collectBody(b => body = b)
58-
.process(bodyStream);
59-
60-
this.result = result as GetAiAgentsResponse;
61-
return body;
57+
return this._parseResponseDefaultAsync(bodyStream);
6258
}
6359
}

src/Documents/Operations/AI/Agents/RunConversationOperation.ts

Lines changed: 31 additions & 66 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@ import { Stream } from "node:stream";
33
import type { AiAgentActionResponse } from "./AiAgentActionResponse.js";
44
import type { AiConversationCreationOptions } from "./AiConversationCreationOptions.js";
55
import type { ConversationResult } from "./ConversationResult.js";
6-
import type { AiUsage } from "./AiUsage.js";
7-
import type { AiAgentActionRequest } from "./AiAgentActionRequest.js";
86
import { RavenCommand } from "../../../../Http/RavenCommand.js";
97
import { DocumentConventions } from "../../../Conventions/DocumentConventions.js";
108
import { IRaftCommand } from "../../../../Http/IRaftCommand.js";
@@ -90,7 +88,6 @@ class RunConversationCommand<TAnswer>
9088
this._options = options;
9189
this._changeVector = changeVector;
9290

93-
// For new conversation IDs (ending with '|'), we need a raft id
9491
if (this._conversationId && this._conversationId.endsWith("|")) {
9592
this._raftId = RaftIdGenerator.newId();
9693
}
@@ -104,80 +101,48 @@ class RunConversationCommand<TAnswer>
104101
return this._raftId;
105102
}
106103

107-
createRequest(node: ServerNode): HttpRequestParameters {
108-
const uriParams = new URLSearchParams({
109-
conversationId: this._conversationId,
110-
agentId: this._agentId,
111-
});
104+
createRequest(node: ServerNode): HttpRequestParameters {
105+
const uriParams = new URLSearchParams({
106+
conversationId: this._conversationId,
107+
agentId: this._agentId,
108+
});
112109

113-
if (this._changeVector) {
114-
uriParams.append("changeVector", this._changeVector);
115-
}
110+
if (this._changeVector) {
111+
uriParams.append("changeVector", this._changeVector);
112+
}
116113

117-
const uri = `${node.url}/databases/${node.database}/ai/agent?${uriParams}`;
114+
const uri = `${node.url}/databases/${node.database}/ai/agent?${uriParams}`;
118115

119-
const bodyObj = {
120-
ActionResponses: this._actionResponses,
121-
UserPrompt: this._prompt,
122-
CreationOptions: this._options
123-
};
116+
const bodyObj = {
117+
ActionResponses: this._actionResponses,
118+
UserPrompt: this._prompt,
119+
CreationOptions: this._options
120+
};
124121

125-
const headers = this._headers().typeAppJson().build();
122+
const headers = this._headers().typeAppJson().build();
126123

127-
// Serialize to PascalCase but ignore the parameters property in CreationOptions
128-
const serialized = ObjectUtil.transformObjectKeys(bodyObj, {
129-
defaultTransform: ObjectUtil.pascalCase,
130-
ignorePaths: [
131-
new RegExp("^CreationOptions\\.Parameters\\..*$")
132-
]
133-
});
124+
// Serialize properties to PascalCase, except "parameters" in CreationOptions, which must keep user-provided, case-sensitive keys unchanged.
125+
const serialized = ObjectUtil.transformObjectKeys(bodyObj, {
126+
defaultTransform: ObjectUtil.pascalCase,
127+
ignorePaths: [
128+
new RegExp("^CreationOptions\\.Parameters\\..*$")
129+
]
130+
});
134131

135132

136-
return {
137-
method: "POST",
138-
uri,
139-
headers,
140-
body: JSON.stringify(serialized)
141-
};
142-
}
133+
return {
134+
method: "POST",
135+
uri,
136+
headers,
137+
body: JsonSerializer.getDefault().serialize(serialized)
138+
};
139+
}
143140

144141
async setResponseAsync(bodyStream: Stream, fromCache: boolean): Promise<string> {
145142
if (!bodyStream) {
146143
this._throwInvalidResponse();
147144
}
148145

149-
let body = "";
150-
const raw = await this._defaultPipeline<any>(_ => body = _).process(bodyStream);
151-
152-
// Normalize server PascalCase payload to our camelCase TS interface
153-
const result: ConversationResult<TAnswer> = {
154-
conversationId: raw?.ConversationId ?? raw?.conversationId,
155-
changeVector: raw?.ChangeVector ?? raw?.changeVector,
156-
response: (raw?.Response ?? raw?.response) as TAnswer,
157-
totalUsage: normalizeUsage(raw?.TotalUsage ?? raw?.totalUsage),
158-
actionRequests: normalizeActionRequests(raw?.ActionRequests ?? raw?.actionRequests)
159-
};
160-
161-
this.result = result;
162-
return body;
146+
return this._parseResponseDefaultAsync(bodyStream)
163147
}
164-
}
165-
166-
function normalizeUsage(u: any): AiUsage {
167-
if (!u) return null;
168-
return {
169-
promptTokens: u.PromptTokens ?? u.promptTokens,
170-
completionTokens: u.CompletionTokens ?? u.completionTokens,
171-
totalTokens: u.TotalTokens ?? u.totalTokens,
172-
cachedTokens: u.CachedTokens ?? u.cachedTokens
173-
} as AiUsage;
174-
}
175-
176-
function normalizeActionRequests(list: any[]): AiAgentActionRequest[] {
177-
if (!Array.isArray(list)) return [];
178-
return list.map(x => ({
179-
name: x.Name ?? x.name,
180-
toolId: x.ToolId ?? x.toolId,
181-
arguments: x.Arguments ?? x.arguments
182-
} as AiAgentActionRequest));
183-
}
148+
}

0 commit comments

Comments
 (0)