From 583c5e31318f2eabf8de9f871854b858839d2d09 Mon Sep 17 00:00:00 2001 From: Darren Reid Date: Wed, 10 Jul 2024 18:28:24 +1000 Subject: [PATCH] Fix tests, swap IStableDiffusion implementation with WIP AiServerClient.cs to handle image generation. --- BlazorDiffusion.Tests/CreativeServiceTests.cs | 24 +- BlazorDiffusion/AiServer/dtos.cs | 1351 +++++++++++++++++ BlazorDiffusion/AiServerClient.cs | 92 ++ BlazorDiffusion/BlazorDiffusion.csproj | 1 + 4 files changed, 1459 insertions(+), 9 deletions(-) create mode 100644 BlazorDiffusion/AiServer/dtos.cs create mode 100644 BlazorDiffusion/AiServerClient.cs diff --git a/BlazorDiffusion.Tests/CreativeServiceTests.cs b/BlazorDiffusion.Tests/CreativeServiceTests.cs index 7bf0d70..ffe724c 100644 --- a/BlazorDiffusion.Tests/CreativeServiceTests.cs +++ b/BlazorDiffusion.Tests/CreativeServiceTests.cs @@ -12,6 +12,8 @@ using ServiceStack.Auth; using ServiceStack.Configuration; using ServiceStack.Data; +using ServiceStack.IO; +using ServiceStack.Messaging; using ServiceStack.OrmLite; namespace BlazorDiffusion.Tests; @@ -31,9 +33,11 @@ public AppHost() : base(nameof(CreativeServiceTests), typeof(MyServices).Assembl public override void Configure(Container container) { - container.Register( - new OrmLiteConnectionFactory("db.sqlite", - SqliteDialect.Provider)); + var dbFactory = new OrmLiteConnectionFactory("db.sqlite", + SqliteDialect.Provider); + dbFactory.RegisterConnection(Databases.Analytics, "analytics.sqlite", + SqliteDialect.Provider); + container.Register(dbFactory); var migrator = CreateMigrator(); migrator.Timeout = TimeSpan.Zero; @@ -66,11 +70,13 @@ public override void Configure(Container container) //IncludeTotal = true, }); - container.Register(new DreamStudioClient - { - ApiKey = Environment.GetEnvironmentVariable("DREAMAI_APIKEY") ?? "", - OutputPathPrefix = Path.Join(ContentRootDirectory.RealPath.CombineWith("App_Files"),"artifacts") - }); + container.Register( + new AiServerClient("https://openai.servicestack.net/",new MemoryVirtualFiles()) + { + OutputPathPrefix = Path.Join(ContentRootDirectory.RealPath.CombineWith("App_Files"),"artifacts") + }); + container.AddSingleton(); + container.AddSingleton(new InMemoryTransientMessageFactory()); container.AddSingleton(c => new OrmLiteCrudEvents(c.Resolve())); container.Resolve().InitSchema(); @@ -156,7 +162,7 @@ public CreativeServiceTests() [Test] [TestCaseSource("AllGenerationCases")] - [Explicit] + //[Explicit] public void Can_generate_images(ImageGenerationTestCase testCase) { var client = CreateClient(); diff --git a/BlazorDiffusion/AiServer/dtos.cs b/BlazorDiffusion/AiServer/dtos.cs new file mode 100644 index 0000000..7fb890a --- /dev/null +++ b/BlazorDiffusion/AiServer/dtos.cs @@ -0,0 +1,1351 @@ +/* Options: +Date: 2024-07-10 07:46:36 +Version: 8.31 +Tip: To override a DTO option, remove "//" prefix before updating +BaseUrl: https://openai.servicestack.net + +//GlobalNamespace: +//MakePartial: True +//MakeVirtual: True +//MakeInternal: False +//MakeDataContractsExtensible: False +//AddNullableAnnotations: False +//AddReturnMarker: True +//AddDescriptionAsComments: True +//AddDataContractAttributes: False +//AddIndexesToDataMembers: False +//AddGeneratedCodeAttributes: False +//AddResponseStatus: False +//AddImplicitVersion: +//InitializeCollections: True +//ExportValueTypes: False +//IncludeTypes: +//ExcludeTypes: +//AddNamespaces: +//AddDefaultXmlNamespace: http://schemas.servicestack.net/types +*/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.Serialization; +using ServiceStack; +using ServiceStack.DataAnnotations; +using AiServer.ServiceModel.Types; +using AiServer.ServiceModel; + +namespace AiServer.ServiceModel +{ + public partial class ApiModel + { + public virtual int Id { get; set; } + public virtual string Name { get; set; } + public virtual string Parameters { get; set; } + public virtual int? ContextSize { get; set; } + public virtual string Website { get; set; } + public virtual string Developer { get; set; } + public virtual string Notes { get; set; } + } + + public partial class ApiProvider + { + public ApiProvider() + { + TaskPaths = new Dictionary{}; + Models = new List{}; + } + + public virtual int Id { get; set; } + public virtual string Name { get; set; } + public virtual int ApiTypeId { get; set; } + public virtual string ApiKey { get; set; } + public virtual string ApiKeyHeader { get; set; } + public virtual string ApiBaseUrl { get; set; } + public virtual string HeartbeatUrl { get; set; } + public virtual Dictionary TaskPaths { get; set; } + public virtual int Concurrency { get; set; } + public virtual int Priority { get; set; } + public virtual bool Enabled { get; set; } + public virtual DateTime? OfflineDate { get; set; } + public virtual DateTime CreatedDate { get; set; } + public virtual ApiType ApiType { get; set; } + public virtual List Models { get; set; } + } + + public partial class ApiProviderModel + { + public virtual int Id { get; set; } + public virtual int ApiProviderId { get; set; } + public virtual string Model { get; set; } + public virtual string ApiModel { get; set; } + } + + public partial class ApiType + { + public ApiType() + { + TaskPaths = new Dictionary{}; + ApiModels = new Dictionary{}; + } + + public virtual int Id { get; set; } + public virtual string Name { get; set; } + public virtual string Website { get; set; } + public virtual string ApiBaseUrl { get; set; } + public virtual string HeartbeatUrl { get; set; } + public virtual string OpenAiProvider { get; set; } + public virtual Dictionary TaskPaths { get; set; } + public virtual Dictionary ApiModels { get; set; } + } + + public partial class ChangeApiProviderStatus + : IReturn, IPost + { + public virtual string Provider { get; set; } + public virtual bool Online { get; set; } + } + + public partial class ChatApiProvider + : IReturn, IPost + { + public virtual string Provider { get; set; } + public virtual string Model { get; set; } + public virtual OpenAiChat Request { get; set; } + public virtual string Prompt { get; set; } + } + + public partial class ChatFailedTasks + : IReturn, IPost + { + public ChatFailedTasks() + { + RequeueFailedTaskIds = new List{}; + } + + public virtual bool? ResetErrorState { get; set; } + public virtual List RequeueFailedTaskIds { get; set; } + } + + public partial class ChatNotifyCompletedTasks + : IReturn, IPost + { + public ChatNotifyCompletedTasks() + { + Ids = new List{}; + } + + [Validate("NotEmpty")] + public virtual List Ids { get; set; } + } + + public partial class ChatNotifyCompletedTasksResponse + { + public ChatNotifyCompletedTasksResponse() + { + Errors = new Dictionary{}; + Results = new List{}; + } + + public virtual Dictionary Errors { get; set; } + public virtual List Results { get; set; } + public virtual ResponseStatus ResponseStatus { get; set; } + } + + public partial class ChatOperations + : IReturn, IPost + { + public virtual bool? ResetTaskQueue { get; set; } + public virtual bool? RequeueIncompleteTasks { get; set; } + } + + public partial class Choice + { + /// + ///The reason the model stopped generating tokens. This will be stop if the model hit a natural stop point or a provided stop sequence, length if the maximum number of tokens specified in the request was reached, content_filter if content was omitted due to a flag from our content filters, tool_calls if the model called a tool + /// + [DataMember(Name="finish_reason")] + public virtual string FinishReason { get; set; } + + /// + ///The index of the choice in the list of choices. + /// + [DataMember(Name="index")] + public virtual int Index { get; set; } + + /// + ///A chat completion message generated by the model. + /// + [DataMember(Name="message")] + public virtual ChoiceMessage Message { get; set; } + } + + [DataContract] + public partial class ChoiceMessage + { + public ChoiceMessage() + { + ToolCalls = new ToolCall[]{}; + } + + /// + ///The contents of the message. + /// + [DataMember(Name="content")] + public virtual string Content { get; set; } + + /// + ///The tool calls generated by the model, such as function calls. + /// + [DataMember(Name="tool_calls")] + public virtual ToolCall[] ToolCalls { get; set; } + + /// + ///The role of the author of this message. + /// + [DataMember(Name="role")] + public virtual string Role { get; set; } + } + + public partial class CompleteOpenAiChat + : IReturn, IPost + { + public virtual long Id { get; set; } + public virtual string Provider { get; set; } + public virtual int DurationMs { get; set; } + public virtual OpenAiChatResponse Response { get; set; } + } + + public partial class CreateApiKey + : IReturn, IPost + { + public CreateApiKey() + { + Scopes = new List{}; + Meta = new Dictionary{}; + } + + public virtual string Key { get; set; } + public virtual string Name { get; set; } + public virtual string UserId { get; set; } + public virtual string UserName { get; set; } + public virtual List Scopes { get; set; } + public virtual string Notes { get; set; } + public virtual int? RefId { get; set; } + public virtual string RefIdStr { get; set; } + public virtual Dictionary Meta { get; set; } + } + + public partial class CreateApiKeyResponse + { + public virtual int Id { get; set; } + public virtual string Key { get; set; } + public virtual string Name { get; set; } + public virtual string UserId { get; set; } + public virtual string UserName { get; set; } + public virtual string VisibleKey { get; set; } + public virtual DateTime CreatedDate { get; set; } + public virtual DateTime? ExpiryDate { get; set; } + public virtual DateTime? CancelledDate { get; set; } + public virtual string Notes { get; set; } + } + + /// + ///Different Models available for the API + /// + public partial class CreateApiModel + : IReturn, ICreateDb + { + public virtual string Name { get; set; } + public virtual string Parameters { get; set; } + public virtual string Website { get; set; } + public virtual int? ContextSize { get; set; } + public virtual string Developer { get; set; } + public virtual string Notes { get; set; } + } + + /// + ///Create an API Provider that can process AI Tasks + /// + public partial class CreateApiProvider + : IReturn, ICreateDb + { + /// + ///The unique name for this API Provider + /// + public virtual string Name { get; set; } + /// + ///The behavior for this API Provider + /// + public virtual int ApiTypeId { get; set; } + /// + ///The API Key to use for this Provider + /// + public virtual string ApiKey { get; set; } + /// + ///Send the API Key in the Header instead of Authorization Bearer + /// + public virtual string ApiKeyHeader { get; set; } + /// + ///The Base URL for the API Provider + /// + public virtual string ApiBaseUrl { get; set; } + /// + ///The URL to check if the API Provider is still online + /// + public virtual string HeartbeatUrl { get; set; } + /// + ///Override API Paths for different AI Tasks + /// + public virtual Dictionary TaskPaths { get; set; } + /// + ///How many requests should be made concurrently + /// + public virtual int Concurrency { get; set; } + /// + ///What priority to give this Provider to use for processing models + /// + public virtual int Priority { get; set; } + /// + ///Whether the Provider is enabled + /// + public virtual bool Enabled { get; set; } + /// + ///The models this API Provider can process + /// + public virtual List Models { get; set; } + } + + /// + ///Register a Model supported by an API Provider + /// + public partial class CreateApiProviderModel + : IReturn, ICreateDb + { + /// + ///The ApiProvider Id + /// + public virtual int ApiProviderId { get; set; } + /// + ///Supported ApiModel Name + /// + public virtual string Model { get; set; } + /// + ///Model to use when sending requests to the API Provider + /// + public virtual string ApiModel { get; set; } + } + + public partial class CreateOpenAiChat + : IReturn, ICreateDb + { + public virtual string RefId { get; set; } + public virtual string Provider { get; set; } + public virtual string ReplyTo { get; set; } + public virtual string Tag { get; set; } + public virtual OpenAiChat Request { get; set; } + } + + public partial class CreateOpenAiChatResponse + { + public virtual long Id { get; set; } + public virtual string RefId { get; set; } + public virtual ResponseStatus ResponseStatus { get; set; } + } + + /// + ///Delete a Model supported by the API Provider + /// + public partial class DeleteApiProviderModel + : IReturn, IDeleteDb + { + /// + ///The ApiProviderModel Id + /// + public virtual int Id { get; set; } + } + + public partial class FetchOpenAiChatRequests + : IReturn, IPost + { + public FetchOpenAiChatRequests() + { + Models = new string[]{}; + } + + [Validate("NotEmpty")] + public virtual string[] Models { get; set; } + + [Validate("NotEmpty")] + public virtual string Provider { get; set; } + + public virtual string Worker { get; set; } + public virtual int? Take { get; set; } + } + + public partial class FetchOpenAiChatRequestsResponse + { + public FetchOpenAiChatRequestsResponse() + { + Results = new OpenAiChatRequest[]{}; + } + + public virtual OpenAiChatRequest[] Results { get; set; } + public virtual ResponseStatus ResponseStatus { get; set; } + } + + public partial class FirePeriodicTask + : IReturn, IPost + { + public virtual PeriodicFrequency Frequency { get; set; } + } + + public partial class GetActiveProviders + : IReturn, IGet + { + } + + public partial class GetActiveProvidersResponse + { + public GetActiveProvidersResponse() + { + Results = new ApiProvider[]{}; + } + + public virtual ApiProvider[] Results { get; set; } + public virtual ResponseStatus ResponseStatus { get; set; } + } + + public partial class GetApiWorkerStats + : IReturn, IGet + { + } + + public partial class GetApiWorkerStatsResponse + { + public GetApiWorkerStatsResponse() + { + Results = new List{}; + } + + public virtual List Results { get; set; } + public virtual ResponseStatus ResponseStatus { get; set; } + } + + public partial class GetOpenAiChat + : IReturn, IGet + { + public virtual int? Id { get; set; } + public virtual string RefId { get; set; } + } + + public partial class GetOpenAiChatResponse + { + public virtual OpenAiChatTask Result { get; set; } + public virtual ResponseStatus ResponseStatus { get; set; } + } + + public partial class GetSummaryStats + : IReturn, IGet + { + public virtual DateTime? From { get; set; } + public virtual DateTime? To { get; set; } + } + + public partial class GetSummaryStatsResponse + { + public GetSummaryStatsResponse() + { + ProviderStats = new List{}; + ModelStats = new List{}; + MonthStats = new List{}; + } + + public virtual List ProviderStats { get; set; } + public virtual List ModelStats { get; set; } + public virtual List MonthStats { get; set; } + } + + [Route("/hello/{Name}")] + public partial class Hello + : IReturn, IGet + { + public virtual string Name { get; set; } + } + + public partial class HelloResponse + { + public virtual string Result { get; set; } + } + + /// + ///Given a list of messages comprising a conversation, the model will return a response. + /// + [DataContract] + public partial class OpenAiChat + { + public OpenAiChat() + { + Messages = new List{}; + LogitBias = new Dictionary{}; + Stop = new List{}; + Tools = new List{}; + } + + /// + ///A list of messages comprising the conversation so far. + /// + [DataMember(Name="messages")] + public virtual List Messages { get; set; } + + /// + ///ID of the model to use. See the model endpoint compatibility table for details on which models work with the Chat API + /// + [DataMember(Name="model")] + public virtual string Model { get; set; } + + /// + ///Number between `-2.0` and `2.0`. Positive values penalize new tokens based on their existing frequency in the text so far, decreasing the model's likelihood to repeat the same line verbatim. + /// + [DataMember(Name="frequency_penalty")] + public virtual double? FrequencyPenalty { get; set; } + + /// + ///Modify the likelihood of specified tokens appearing in the completion. + /// + [DataMember(Name="logit_bias")] + public virtual Dictionary LogitBias { get; set; } + + /// + ///Whether to return log probabilities of the output tokens or not. If true, returns the log probabilities of each output token returned in the content of message. + /// + [DataMember(Name="logprobs")] + public virtual bool? LogProbs { get; set; } + + /// + ///An integer between 0 and 20 specifying the number of most likely tokens to return at each token position, each with an associated log probability. logprobs must be set to true if this parameter is used. + /// + [DataMember(Name="top_logprobs")] + public virtual int? TopLogProbs { get; set; } + + /// + ///The maximum number of tokens that can be generated in the chat completion. + /// + [DataMember(Name="max_tokens")] + public virtual int? MaxTokens { get; set; } + + /// + ///How many chat completion choices to generate for each input message. Note that you will be charged based on the number of generated tokens across all of the choices. Keep `n` as `1` to minimize costs. + /// + [DataMember(Name="n")] + public virtual int? N { get; set; } + + /// + ///Number between -2.0 and 2.0. Positive values penalize new tokens based on whether they appear in the text so far, increasing the model's likelihood to talk about new topics. + /// + [DataMember(Name="presence_penalty")] + public virtual double? PresencePenalty { get; set; } + + /// + ///An object specifying the format that the model must output. Compatible with GPT-4 Turbo and all GPT-3.5 Turbo models newer than `gpt-3.5-turbo-1106`. Setting Type to ResponseFormat.JsonObject enables JSON mode, which guarantees the message the model generates is valid JSON. + /// + [DataMember(Name="response_format")] + public virtual OpenAiResponseFormat ResponseFormat { get; set; } + + /// + ///This feature is in Beta. If specified, our system will make a best effort to sample deterministically, such that repeated requests with the same seed and parameters should return the same result. Determinism is not guaranteed, and you should refer to the system_fingerprint response parameter to monitor changes in the backend. + /// + [DataMember(Name="seed")] + public virtual int? Seed { get; set; } + + /// + ///Up to 4 sequences where the API will stop generating further tokens. + /// + [DataMember(Name="stop")] + public virtual List Stop { get; set; } + + /// + ///If set, partial message deltas will be sent, like in ChatGPT. Tokens will be sent as data-only server-sent events as they become available, with the stream terminated by a `data: [DONE]` message. + /// + [DataMember(Name="stream")] + public virtual bool? Stream { get; set; } + + /// + ///What sampling temperature to use, between 0 and 2. Higher values like 0.8 will make the output more random, while lower values like 0.2 will make it more focused and deterministic. + /// + [DataMember(Name="temperature")] + public virtual double? Temperature { get; set; } + + /// + ///An alternative to sampling with temperature, called nucleus sampling, where the model considers the results of the tokens with top_p probability mass. So 0.1 means only the tokens comprising the top 10% probability mass are considered. + /// + [DataMember(Name="top_p")] + public virtual double TopP { get; set; } + + /// + ///A list of tools the model may call. Currently, only functions are supported as a tool. Use this to provide a list of functions the model may generate JSON inputs for. A max of 128 functions are supported. + /// + [DataMember(Name="tools")] + public virtual List Tools { get; set; } + + /// + ///A unique identifier representing your end-user, which can help OpenAI to monitor and detect abuse. + /// + [DataMember(Name="user")] + public virtual string User { get; set; } + } + + [DataContract] + public partial class OpenAiChatResponse + { + public OpenAiChatResponse() + { + Choices = new List{}; + } + + /// + ///A unique identifier for the chat completion. + /// + [DataMember(Name="id")] + public virtual string Id { get; set; } + + /// + ///A list of chat completion choices. Can be more than one if n is greater than 1. + /// + [DataMember(Name="choices")] + public virtual List Choices { get; set; } + + /// + ///The Unix timestamp (in seconds) of when the chat completion was created. + /// + [DataMember(Name="created")] + public virtual long Created { get; set; } + + /// + ///The model used for the chat completion. + /// + [DataMember(Name="model")] + public virtual string Model { get; set; } + + /// + ///This fingerprint represents the backend configuration that the model runs with. + /// + [DataMember(Name="system_fingerprint")] + public virtual string SystemFingerprint { get; set; } + + /// + ///The object type, which is always chat.completion. + /// + [DataMember(Name="object")] + public virtual string Object { get; set; } + + /// + ///Usage statistics for the completion request. + /// + [DataMember(Name="usage")] + public virtual OpenAiUsage Usage { get; set; } + } + + /// + ///A list of messages comprising the conversation so far. + /// + [DataContract] + public partial class OpenAiMessage + { + public OpenAiMessage() + { + ToolCalls = new ToolCall[]{}; + } + + /// + ///The contents of the message. + /// + [DataMember(Name="content")] + public virtual string Content { get; set; } + + /// + ///The role of the author of this message. Valid values are `system`, `user`, `assistant` and `tool`. + /// + [DataMember(Name="role")] + public virtual string Role { get; set; } + + /// + ///An optional name for the participant. Provides the model information to differentiate between participants of the same role. + /// + [DataMember(Name="name")] + public virtual string Name { get; set; } + + /// + ///The tool calls generated by the model, such as function calls. + /// + [DataMember(Name="tool_calls")] + public virtual ToolCall[] ToolCalls { get; set; } + + /// + ///Tool call that this message is responding to. + /// + [DataMember(Name="tool_call_id")] + public virtual string ToolCallId { get; set; } + } + + [DataContract] + public partial class OpenAiResponseFormat + { + /// + ///An object specifying the format that the model must output. Compatible with GPT-4 Turbo and all GPT-3.5 Turbo models newer than gpt-3.5-turbo-1106. + /// + [DataMember(Name="response_format")] + public virtual ResponseFormat Type { get; set; } + } + + [DataContract] + public partial class OpenAiTools + { + /// + ///The type of the tool. Currently, only function is supported. + /// + [DataMember(Name="type")] + public virtual OpenAiToolType Type { get; set; } + } + + public enum OpenAiToolType + { + [EnumMember(Value="function")] + Function, + } + + /// + ///Usage statistics for the completion request. + /// + [DataContract] + public partial class OpenAiUsage + { + /// + ///Number of tokens in the generated completion. + /// + [DataMember(Name="completion_tokens")] + public virtual int CompletionTokens { get; set; } + + /// + ///Number of tokens in the prompt. + /// + [DataMember(Name="prompt_tokens")] + public virtual int PromptTokens { get; set; } + + /// + ///Total number of tokens used in the request (prompt + completion). + /// + [DataMember(Name="total_tokens")] + public virtual int TotalTokens { get; set; } + } + + /// + ///Different Models available in AI Server + /// + public partial class QueryApiModels + : QueryDb, IReturn> + { + } + + /// + ///View and API Provider Models + /// + public partial class QueryApiProviderModels + : QueryDb, IReturn> + { + public virtual int? ApiProviderId { get; set; } + public virtual string Model { get; set; } + public virtual string ApiModel { get; set; } + } + + /// + ///API Providers that can process AI Tasks + /// + public partial class QueryApiProviders + : QueryDb, IReturn> + { + public virtual string Name { get; set; } + } + + /// + ///The Type and behavior of different API Providers + /// + public partial class QueryApiType + : QueryDb, IReturn> + { + } + + public partial class QueryCompletedChatTasks + : QueryDb, IReturn> + { + public virtual string Db { get; set; } + public virtual int? Id { get; set; } + public virtual string RefId { get; set; } + } + + public partial class QueryFailedChatTasks + : QueryDb, IReturn> + { + public virtual string Db { get; set; } + } + + public partial class QueryOpenAiChat + : QueryDb, IReturn> + { + public virtual int? Id { get; set; } + public virtual string RefId { get; set; } + } + + public partial class QueryTaskSummary + : QueryDb, IReturn> + { + } + + public partial class RerunCompletedTasks + : IReturn, IPost + { + public RerunCompletedTasks() + { + Ids = new List{}; + } + + public virtual List Ids { get; set; } + } + + public partial class RerunCompletedTasksResponse + { + public RerunCompletedTasksResponse() + { + Errors = new Dictionary{}; + Results = new List{}; + } + + public virtual Dictionary Errors { get; set; } + public virtual List Results { get; set; } + public virtual ResponseStatus ResponseStatus { get; set; } + } + + public partial class ResetActiveProviders + : IReturn, IGet + { + } + + public enum ResponseFormat + { + [EnumMember(Value="text")] + Text, + [EnumMember(Value="json_object")] + JsonObject, + } + + public partial class RestartWorkers + : IReturn, IPost + { + } + + public partial class StartWorkers + : IReturn, IPost + { + } + + public partial class StopWorkers + : IReturn, IPost + { + } + + public partial class SummaryStats + { + public virtual string Name { get; set; } + public virtual int TotalTasks { get; set; } + public virtual int TotalPromptTokens { get; set; } + public virtual int TotalCompletionTokens { get; set; } + public virtual double TotalMinutes { get; set; } + public virtual double TokensPerSecond { get; set; } + } + + public partial class TaskBase + { + public virtual long Id { get; set; } + public virtual string Model { get; set; } + public virtual string Provider { get; set; } + public virtual string RefId { get; set; } + public virtual string Tag { get; set; } + public virtual string ReplyTo { get; set; } + public virtual DateTime CreatedDate { get; set; } + public virtual string CreatedBy { get; set; } + public virtual string Worker { get; set; } + public virtual string WorkerIp { get; set; } + public virtual string RequestId { get; set; } + public virtual DateTime? StartedDate { get; set; } + public virtual DateTime? CompletedDate { get; set; } + public virtual int DurationMs { get; set; } + public virtual int? RetryLimit { get; set; } + public virtual int Retries { get; set; } + public virtual DateTime? NotificationDate { get; set; } + public virtual string ErrorCode { get; set; } + public virtual ResponseStatus Error { get; set; } + } + + public partial class TaskSummary + { + public virtual long Id { get; set; } + public virtual TaskType Type { get; set; } + public virtual string Model { get; set; } + public virtual string Provider { get; set; } + public virtual string RefId { get; set; } + public virtual string Tag { get; set; } + public virtual int PromptTokens { get; set; } + public virtual int CompletionTokens { get; set; } + public virtual int DurationMs { get; set; } + public virtual DateTime CreatedDate { get; set; } + } + + public enum TaskType + { + OpenAiChat = 1, + } + + /// + ///The tool calls generated by the model, such as function calls. + /// + [DataContract] + public partial class ToolCall + { + /// + ///The ID of the tool call. + /// + [DataMember(Name="id")] + public virtual string Id { get; set; } + + /// + ///The type of the tool. Currently, only `function` is supported. + /// + [DataMember(Name="type")] + public virtual string Type { get; set; } + + /// + ///The function that the model called. + /// + [DataMember(Name="function")] + public virtual string Function { get; set; } + } + + public partial class UpdateApiProvider + : IReturn, IPatchDb + { + public virtual int Id { get; set; } + public virtual string ApiKey { get; set; } + public virtual string ApiBaseUrl { get; set; } + public virtual string HeartbeatUrl { get; set; } + public virtual int? Concurrency { get; set; } + public virtual int? Priority { get; set; } + public virtual bool? Enabled { get; set; } + } + + /// + ///Update the Model supported by the API Provider + /// + public partial class UpdateApiProviderModel + : IReturn, IPatchDb + { + /// + ///The ApiProviderModel Id + /// + public virtual int Id { get; set; } + /// + ///The ApiProvider Id + /// + public virtual int? ApiProviderId { get; set; } + /// + ///Supported ApiModel Name + /// + public virtual string Model { get; set; } + /// + ///Model to use when sending requests to the API Provider + /// + public virtual string ApiModel { get; set; } + } + + public partial class WorkerStats + { + public virtual string Name { get; set; } + public virtual long Queued { get; set; } + public virtual long Received { get; set; } + public virtual long Completed { get; set; } + public virtual long Retries { get; set; } + public virtual long Failed { get; set; } + public virtual DateTime? Offline { get; set; } + public virtual bool Running { get; set; } + } + +} + +namespace AiServer.ServiceModel.Types +{ + [DataContract] + public enum ArtStyle + { + [EnumMember(Value="3d_model")] + ThreeDModel, + [EnumMember(Value="analog_film")] + AnalogFilm, + [EnumMember(Value="anime")] + Anime, + [EnumMember(Value="cinematic")] + Cinematic, + [EnumMember(Value="comic")] + ComicBook, + [EnumMember(Value="digital_art")] + DigitalArt, + [EnumMember(Value="enhance")] + Enhance, + [EnumMember(Value="fantasy_art")] + FantasyArt, + [EnumMember(Value="isometric")] + Isometric, + [EnumMember(Value="line_art")] + LineArt, + [EnumMember(Value="low_poly")] + LowPoly, + [EnumMember(Value="modeling_compound")] + ModelingCompound, + [EnumMember(Value="neon_punk")] + NeonPunk, + [EnumMember(Value="oil_painting")] + Origami, + [EnumMember(Value="photographic")] + Photographic, + [EnumMember(Value="pixel_art")] + PixelArt, + [EnumMember(Value="game_asset")] + TileTexture, + } + + public partial class ComfyFileInput + { + public virtual string Name { get; set; } + public virtual string Type { get; set; } + public virtual string Subfolder { get; set; } + } + + public partial class ComfyFileOutput + { + public virtual string Filename { get; set; } + public virtual string Type { get; set; } + public virtual string Subfolder { get; set; } + } + + public partial class ComfyHostedFileOutput + { + public virtual string Url { get; set; } + public virtual string FileName { get; set; } + } + + public partial class ComfyImageToImage + : IReturn + { + public virtual long Seed { get; set; } + public virtual double CfgScale { get; set; } + public virtual ComfySampler Sampler { get; set; } + public virtual int Steps { get; set; } + public virtual int BatchSize { get; set; } + public virtual double Denoise { get; set; } + public virtual string Scheduler { get; set; } + public virtual string Model { get; set; } + public virtual string PositivePrompt { get; set; } + public virtual string NegativePrompt { get; set; } + public virtual Stream ImageInput { get; set; } + } + + public partial class ComfyImageToImageResponse + { + public virtual string FilePath { get; set; } + } + + public partial class ComfyImageToImageUpscale + : IReturn + { + public virtual string UpscaleModel { get; set; } + public virtual ComfyFileInput Image { get; set; } + public virtual Stream ImageInput { get; set; } + } + + public partial class ComfyImageToImageUpscaleResponse + { + public virtual string FilePath { get; set; } + } + + public partial class ComfyImageToImageWithMask + : IReturn + { + public virtual long Seed { get; set; } + public virtual double CfgScale { get; set; } + public virtual ComfySampler Sampler { get; set; } + public virtual int Steps { get; set; } + public virtual int BatchSize { get; set; } + public virtual double Denoise { get; set; } + public virtual string Scheduler { get; set; } + public virtual string Model { get; set; } + public virtual string PositivePrompt { get; set; } + public virtual string NegativePrompt { get; set; } + public virtual ComfyMaskSource MaskChannel { get; set; } + public virtual Stream ImageInput { get; set; } + public virtual Stream MaskInput { get; set; } + } + + public partial class ComfyImageToImageWithMaskResponse + { + public virtual string FilePath { get; set; } + } + + public partial class ComfyImageToText + : IReturn + { + public virtual Stream ImageInput { get; set; } + } + + public partial class ComfyImageToTextResponse + { + public virtual string Text { get; set; } + } + + public enum ComfyMaskSource + { + red, + blue, + green, + alpha, + } + + public partial class ComfyOutput + { + public ComfyOutput() + { + Files = new List{}; + Texts = new List{}; + } + + public virtual List Files { get; set; } + public virtual List Texts { get; set; } + } + + public enum ComfySampler + { + euler, + euler_ancestral, + huen, + huenpp2, + dpm_2, + dpm_2_ancestral, + lms, + dpm_fast, + dpm_adaptive, + dpmpp_2s_ancestral, + dpmpp_sde, + dpmpp_sde_gpu, + dpmpp_2m, + dpmpp_2m_sde, + dpmpp_2m_sde_gpu, + dpmpp_3m_sde, + dpmpp_3m_sde_gpu, + ddpm, + lcm, + ddim, + uni_pc, + uni_pc_bh2, + } + + public partial class ComfySpeechToText + : IReturn + { + public virtual string Model { get; set; } + public virtual Stream SpeechInput { get; set; } + } + + public partial class ComfySpeechToTextResponse + { + public virtual string Text { get; set; } + } + + public enum ComfyTaskType + { + TextToImage = 1, + ImageToImage = 2, + ImageToImageUpscale = 3, + ImageToImageWithMask = 4, + ImageToText = 5, + TextToAudio = 6, + TextToSpeech = 7, + SpeechToText = 8, + } + + public partial class ComfyTextOutput + { + public virtual string Text { get; set; } + } + + public partial class ComfyTextToAudio + : IReturn + { + public virtual string Clip { get; set; } + public virtual string Model { get; set; } + public virtual int? Steps { get; set; } + public virtual double? CfgScale { get; set; } + public virtual int? Seed { get; set; } + public virtual ComfySampler? Sampler { get; set; } + public virtual string Scheduler { get; set; } + public virtual string PositivePrompt { get; set; } + public virtual string NegativePrompt { get; set; } + public virtual double? SampleLength { get; set; } + } + + public partial class ComfyTextToAudioResponse + { + public virtual string FilePath { get; set; } + } + + public partial class ComfyTextToImage + : IReturn + { + public virtual long Seed { get; set; } + public virtual double CfgScale { get; set; } + public virtual int Height { get; set; } + public virtual int Width { get; set; } + public virtual ComfySampler Sampler { get; set; } + public virtual int BatchSize { get; set; } + public virtual int Steps { get; set; } + public virtual string Model { get; set; } + public virtual string PositivePrompt { get; set; } + public virtual string NegativePrompt { get; set; } + public virtual string Scheduler { get; set; } + } + + public partial class ComfyTextToImageResponse + { + public ComfyTextToImageResponse() + { + Images = new List{}; + } + + public virtual List Images { get; set; } + } + + public partial class ComfyTextToSpeech + : IReturn + { + public virtual string PositivePrompt { get; set; } + public virtual string Model { get; set; } + } + + public partial class ComfyTextToSpeechResponse + { + public virtual string FilePath { get; set; } + } + + public partial class ComfyWorkflowResponse + { + public ComfyWorkflowResponse() + { + NodeErrors = new List{}; + } + + public virtual string PromptId { get; set; } + public virtual int Number { get; set; } + public virtual List NodeErrors { get; set; } + } + + public partial class ComfyWorkflowStatus + { + public ComfyWorkflowStatus() + { + Outputs = new List{}; + } + + public virtual string StatusMessage { get; set; } + public virtual bool Completed { get; set; } + public virtual List Outputs { get; set; } + } + + public partial class NodeError + { + } + + public partial class OpenAiChatCompleted + : OpenAiChatTask + { + } + + public partial class OpenAiChatFailed + : OpenAiChatTask + { + public virtual DateTime FailedDate { get; set; } + } + + public partial class OpenAiChatRequest + { + public virtual long Id { get; set; } + public virtual string Model { get; set; } + public virtual string Provider { get; set; } + public virtual OpenAiChat Request { get; set; } + } + + public partial class OpenAiChatTask + : TaskBase + { + public virtual OpenAiChat Request { get; set; } + public virtual OpenAiChatResponse Response { get; set; } + } + + public enum PeriodicFrequency + { + Minute, + Hourly, + Daily, + Monthly, + } + + public partial class QueueComfyWorkflow + : IReturn + { + public virtual string Model { get; set; } + public virtual int? Steps { get; set; } + public virtual int? BatchSize { get; set; } + public virtual int? Seed { get; set; } + public virtual string PositivePrompt { get; set; } + public virtual string NegativePrompt { get; set; } + public virtual Stream ImageInput { get; set; } + public virtual Stream SpeechInput { get; set; } + public virtual Stream MaskInput { get; set; } + public virtual ComfySampler? Sampler { get; set; } + public virtual ArtStyle? ArtStyle { get; set; } + public virtual string Scheduler { get; set; } + public virtual int? CfgScale { get; set; } + public virtual double? Denoise { get; set; } + public virtual string UpscaleModel { get; set; } + public virtual int? Width { get; set; } + public virtual int? Height { get; set; } + public virtual string Clip { get; set; } + public virtual double? SampleLength { get; set; } + public virtual ComfyTaskType TaskType { get; set; } + public virtual string RefId { get; set; } + public virtual string Provider { get; set; } + public virtual string ReplyTo { get; set; } + public virtual string Tag { get; set; } + } + + public partial class QueueComfyWorkflowResponse + { + public QueueComfyWorkflowResponse() + { + FileOutputs = new List{}; + TextOutputs = new List{}; + } + + public virtual ComfyWorkflowStatus Status { get; set; } + public virtual ComfyWorkflowResponse WorkflowResponse { get; set; } + public virtual string PromptId { get; set; } + public virtual List FileOutputs { get; set; } + public virtual List TextOutputs { get; set; } + } + +} + diff --git a/BlazorDiffusion/AiServerClient.cs b/BlazorDiffusion/AiServerClient.cs new file mode 100644 index 0000000..41d63f3 --- /dev/null +++ b/BlazorDiffusion/AiServerClient.cs @@ -0,0 +1,92 @@ +using AiServer.ServiceModel.Types; +using BlazorDiffusion.ServiceInterface; +using BlazorDiffusion.ServiceModel; +using ServiceStack.IO; +using JsonApiClient = ServiceStack.JsonApiClient; + +namespace BlazorDiffusion; + +public class AiServerClient: IStableDiffusionClient +{ + public JsonApiClient Client { get; } + public IVirtualFiles VirtualFiles { get; } + + public string? OutputPathPrefix { get; set; } + + public AiServerClient(string aiServerBaseUrl,IVirtualFiles virtualFiles) + { + Client = new JsonApiClient(aiServerBaseUrl); + VirtualFiles = virtualFiles; + } + public async Task GenerateImageAsync(ImageGeneration request) + { + var req = request.ToComfy(); + var res = await Client.PostAsync(req); + var now = DateTime.UtcNow; + var key = $"{now:yyyy/MM/dd}/{(long)now.TimeOfDay.TotalMilliseconds}"; + + var promptId = Guid.NewGuid().ToString(); + var results = new List(); + foreach (var item in res.Images) + { + var artifactUrl = $"{Client.BaseUri.TrimEnd('/')}/uploads{item.Url}"; + var seed = (uint)Random.Shared.Next(); + var output = Path.Join(OutputPathPrefix, key, $"output_{Guid.NewGuid()}.png"); + var bytes = await artifactUrl.GetBytesFromUrlAsync(); + await VirtualFiles.WriteFileAsync(output, bytes); + var imageDetails = ImageDetails.Calculate(bytes); + + results.Add(new() + { + Prompt = request.Prompt, + Seed = seed, + AnswerId = promptId, + FilePath = $"/artifacts/{key}/output_{seed}.png", + FileName = $"output_{seed}.png", + ContentLength = bytes.Length, + Width = request.Width, + Height = request.Height, + ImageDetails = imageDetails, + }); + } + + return new ImageGenerationResponse + { + RequestId = Guid.NewGuid().ToString(), + EngineId = "comfy", + Key = key, + Results = results, + }; + } + + public IVirtualFile? GetMetadataFile(Creative creative) + { + throw new NotImplementedException(); + } + + public Task SaveMetadataAsync(Creative entry) + { + throw new NotImplementedException(); + } + + public Task DeleteFolderAsync(Creative entry) + { + throw new NotImplementedException(); + } +} + +public static class StableDiffusionClientExtensions +{ + public static ComfyTextToImage ToComfy(this ImageGeneration request) + { + return new ComfyTextToImage + { + Height = request.Height, + Width = request.Width, + Seed = request.Seed ?? Random.Shared.Next(), + Steps = request.Steps, + BatchSize = request.Images, + PositivePrompt = request.Prompt, + }; + } +} \ No newline at end of file diff --git a/BlazorDiffusion/BlazorDiffusion.csproj b/BlazorDiffusion/BlazorDiffusion.csproj index 7419f9b..d18461a 100644 --- a/BlazorDiffusion/BlazorDiffusion.csproj +++ b/BlazorDiffusion/BlazorDiffusion.csproj @@ -73,6 +73,7 @@ +