Skip to content

Commit f7c8ca6

Browse files
MaxiDonkey@hotmail.comMaxiDonkey@hotmail.com
authored andcommitted
fix streaming error with v1/responses
1 parent 82685d9 commit f7c8ca6

4 files changed

Lines changed: 397 additions & 96 deletions

File tree

source/GenAI.API.pas

Lines changed: 58 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -243,7 +243,7 @@ TApiDeserializer = class(TApiHttpHandler)
243243
/// </remarks>
244244
TGenAIAPI = class(TApiDeserializer)
245245
private
246-
function MockJsonResponse(const FieldName: string; Response: TStringStream): string;
246+
function MockJsonResponse(const FieldName: string; Response: TStringStream): string; overload;
247247
function MockJsonFile(const FieldName: string; Response: TStringStream): string;
248248
public
249249
/// <summary>
@@ -362,6 +362,31 @@ TGenAIAPI = class(TApiDeserializer)
362362
/// </exception>
363363
function Post<TParams: TJSONParam>(const Endpoint: string; ParamProc: TProc<TParams>; Response: TStringStream; Event: TReceiveDataCallback): Boolean; overload;
364364
/// <summary>
365+
/// Sends a POST request with parameters and streams the response.
366+
/// </summary>
367+
/// <typeparam name="TParams">
368+
/// The type of the parameters object for the request.
369+
/// </typeparam>
370+
/// <param name="Endpoint">
371+
/// The relative endpoint to send the POST request to.
372+
/// </param>
373+
/// <param name="ParamProc">
374+
/// A callback procedure to configure the request parameters.
375+
/// </param>
376+
/// <param name="Response">
377+
/// A string stream where the response will be written.
378+
/// </param>
379+
/// <param name="Event">
380+
/// A callback procedure for handling the received data during streaming.
381+
/// </param>
382+
/// <returns>
383+
/// A boolean value indicating whether the request was successful.
384+
/// </returns>
385+
/// <exception cref="GenAIInvalidResponseError">
386+
/// Raised if the response cannot be deserialized or is non-compliant.
387+
/// </exception>
388+
function Post<TParams: TJSONParam>(const Endpoint: string; ParamProc: TProc<TParams>; Response: TStream; Event: TReceiveDataCallback): Boolean; overload;
389+
/// <summary>
365390
/// Sends a POST request with parameters and returns a strongly typed object.
366391
/// </summary>
367392
/// <typeparam name="TResult">
@@ -488,6 +513,38 @@ constructor TGenAIAPI.Create(const AAPIKey: string);
488513
APIKey := AAPIKey;
489514
end;
490515

516+
function TGenAIAPI.Post<TParams>(const Endpoint: string;
517+
ParamProc: TProc<TParams>; Response: TStream;
518+
Event: TReceiveDataCallback): Boolean;
519+
var
520+
Params: TParams;
521+
begin
522+
Monitoring.Inc;
523+
Params := TParams.Create;
524+
try
525+
if Assigned(ParamProc) then
526+
ParamProc(Params);
527+
var Code := HttpClient.Post(BuildUrl(Endpoint), Params.JSON, Response, BuildJsonHeaders, Event);
528+
Result := (Code > 200) and (Code < 299);
529+
case Code of
530+
200..299:
531+
Result := True;
532+
else
533+
begin
534+
Response.Position := 0;
535+
var ErrBytes: TBytes;
536+
SetLength(ErrBytes, Response.Size);
537+
Response.ReadBuffer(ErrBytes, Length(ErrBytes));
538+
DeserializeErrorData(Code, TEncoding.UTF8.GetString(ErrBytes));
539+
end;
540+
end;
541+
finally
542+
Params.Free;
543+
ResetCustomHeader;
544+
Monitoring.Dec;
545+
end;
546+
end;
547+
491548
function TGenAIAPI.Post<TResult, TParams>(const Endpoint: string; ParamProc: TProc<TParams>;
492549
const RawByteFieldName: string): TResult;
493550
begin

source/GenAI.HttpClientAPI.pas

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,28 @@ THttpClientAPI = class(TInterfacedObject, IHttpClientAPI)
142142
/// </returns>
143143
function Post(const URL: string; Body: TJSONObject; Response: TStringStream; const Headers: TNetHeaders; OnReceiveData: TReceiveDataCallback): Integer; overload;
144144
/// <summary>
145+
/// Sends an HTTP POST request with a JSON body to the specified URL and handles a full streamed responses.
146+
/// </summary>
147+
/// <param name="URL">
148+
/// The endpoint URL to send the POST request to.
149+
/// </param>
150+
/// <param name="Body">
151+
/// The JSON object to include in the POST request body.
152+
/// </param>
153+
/// <param name="Response">
154+
/// A string stream to capture the response content.
155+
/// </param>
156+
/// <param name="Headers">
157+
/// A list of HTTP headers to include in the request.
158+
/// </param>
159+
/// <param name="OnReceiveData">
160+
/// A callback procedure to handle data as it is received during the streaming process.
161+
/// </param>
162+
/// <returns>
163+
/// The HTTP status code returned by the server.
164+
/// </returns>
165+
function Post(const URL: string; Body: TJSONObject; Response: TStream; const Headers: TNetHeaders; OnReceiveData: TReceiveDataCallback): Integer; overload;
166+
/// <summary>
145167
/// Sends an HTTP PATCH request with a JSON body to the specified URL.
146168
/// </summary>
147169
/// <param name="URL">
@@ -317,4 +339,25 @@ function THttpClientAPI.Post(const URL: string; Response: TStringStream;
317339
Result := FHttpClient.Post(URL, Stream, Response, Headers).StatusCode;
318340
end;
319341

342+
function THttpClientAPI.Post(const URL: string; Body: TJSONObject;
343+
Response: TStream; const Headers: TNetHeaders;
344+
OnReceiveData: TReceiveDataCallback): Integer;
345+
begin
346+
CheckAPISettings;
347+
348+
{--- Query always encoded in explicit UTF-8 }
349+
var Bytes := TEncoding.UTF8.GetBytes(Body.ToJSON);
350+
var Req := TMemoryStream.Create;
351+
Req.WriteBuffer(Bytes, Length(Bytes));
352+
Req.Position := 0;
353+
354+
FHttpClient.ReceiveDataCallback := OnReceiveData;
355+
try
356+
Result := FHttpClient.Post(URL, Req, Response, Headers).StatusCode;
357+
finally
358+
FHttpClient.ReceiveDataCallback := nil;
359+
Req.Free;
360+
end;
361+
end;
362+
320363
end.

source/GenAI.HttpClientInterface.pas

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -229,6 +229,28 @@ interface
229229
/// </returns>
230230
function Post(const URL: string; Body: TJSONObject; Response: TStringStream; const Headers: TNetHeaders; OnReceiveData: TReceiveDataCallback): Integer; overload;
231231
/// <summary>
232+
/// Sends an HTTP POST request with a JSON body to the specified URL and handles full streamed responses.
233+
/// </summary>
234+
/// <param name="URL">
235+
/// The endpoint URL to send the POST request to.
236+
/// </param>
237+
/// <param name="Body">
238+
/// The JSON object to include in the POST request body.
239+
/// </param>
240+
/// <param name="Response">
241+
/// A string stream to capture the response content.
242+
/// </param>
243+
/// <param name="Headers">
244+
/// A list of HTTP headers to include in the request.
245+
/// </param>
246+
/// <param name="OnReceiveData">
247+
/// A callback procedure to handle data as it is received during the streaming process.
248+
/// </param>
249+
/// <returns>
250+
/// The HTTP status code returned by the server.
251+
/// </returns>
252+
function Post(const URL: string; Body: TJSONObject; Response: TStream; const Headers: TNetHeaders; OnReceiveData: TReceiveDataCallback): Integer; overload;
253+
/// <summary>
232254
/// Sends an HTTP PATCH request with a JSON body to the specified URL.
233255
/// </summary>
234256
/// <param name="URL">

0 commit comments

Comments
 (0)