OpenAIClient.psm1


#!/usr/bin/env pwsh
#region Classes
enum AssistantCollectionOrder {
  Ascending
  Descending
}

enum AssistantResponseFormatKind {
  Auto
  JsonObject
  JsonSchema
  Text
}

enum FileSearchRanker {
  Auto
  Default20240821
}

enum MessageCollectionOrder {
  Ascending
  Descending
}

enum MessageRole {
  User
  Assistant
}

enum MessageStatus {
  Completed
  InProgress
  Incomplete
}

enum MessageFailureReason {
  ContentFilter
  MaxTokens
  RunCancelled
  RunExpired
  RunFailed
}

enum MessageImageDetail {
  Auto
  Low
  High
}

enum RunCollectionOrder {
  Ascending
  Descending
}

enum RunErrorCode {
  InvalidPrompt
  RateLimitExceeded
  ServerError
  ToolCallError
  InvalidRequest
}

enum RunIncompleteReason {
  MaxInputTokenCount
  MaxOutputTokenCount
}

enum RunStatus {
  Cancelled
  Cancelling
  Completed
  Expired
  Failed
  Incomplete
  InProgress
  Queued
  RequiresAction
}

enum RunStepCollectionOrder {
  Ascending
  Descending
}

enum RunStepErrorCode {
  RateLimitExceeded
  ServerError
}

enum RunStepFileSearchResultContentKind {
  Text
}

enum RunStepKind {
  CreatedMessage
  ToolCall
}

enum RunStepStatus {
  Cancelled
  Completed
  Expired
  Failed
  InProgress
}

enum RunStepToolCallKind {
  CodeInterpreter
  FileSearch
  Function
}

enum StreamingUpdateReason {
  Unknown
  ThreadCreated
  RunCreated
  RunQueued
  RunInProgress
  RunRequiresAction
  RunCompleted
  RunIncomplete
  RunFailed
  RunCancelling
  RunCancelled
  RunExpired
  RunStepCreated
  RunStepInProgress
  RunStepUpdated
  RunStepCompleted
  RunStepFailed
  RunStepCancelled
  RunStepExpired
  MessageCreated
  MessageInProgress
  MessageUpdated
  MessageCompleted
  MessageFailed
  Error
  Done
}

enum FileUploadPurpose {
  Assistants
  FineTuning
  Vision
  UserData
  Evaluations
  Batch
}

enum FilePurpose {
  Assistants
  AssistantsOutput
  Batch
  BatchOutput
  FineTune
  FineTuneResults
  Vision
  UserData
  Evaluations
}

enum FileStatus {
  Uploaded
  Processed
  Error
}

enum GeneratedImageFormat {
  Bytes
  Uri
}

enum GeneratedImageQuality {
  High
  Standard
}

enum GeneratedImageStyle {
  Natural
  Vivid
}

enum ConversationAudioFormat {
  G711Alaw
  G711Ulaw
  Pcm16
}

enum ConversationContentModalities {
  Default
  Text
  Audio
}

enum ConversationContentPartKind {
  InputAudio
  InputText
  OutputAudio
  OutputText
}

enum ConversationIncompleteReason {
  ClientCancelled
  ContentFilter
  MaxOutputTokens
  TurnDetected
}

enum ConversationItemStatus {
  Completed
  Incomplete
  InProgress
}

enum ConversationMessageRole {
  Assistant
  System
  User
}

enum ConversationStatus {
  Cancelled
  Completed
  Failed
  Incomplete
}

enum ConversationToolChoiceKind {
  Unknown
  Auto
  None
  Required
  Function
}

enum ConversationToolKind {
  Function
}

enum ConversationTranscriptionModel {
  Whisper1
}

enum ConversationTurnDetectionKind {
  ServerVoiceActivityDetection
  Disabled
}

enum ConversationUpdateKind {
  Unknown
  SessionStarted
  SessionConfigured
  ItemCreated
  ConversationCreated
  ItemDeleted
  ItemTruncated
  ResponseStarted
  ResponseFinished
  RateLimitsUpdated
  ItemStreamingStarted
  ItemStreamingFinished
  ItemContentPartStarted
  ItemContentPartFinished
  ItemStreamingPartAudioDelta
  ItemStreamingPartAudioFinished
  ItemStreamingPartAudioTranscriptionDelta
  ItemStreamingPartAudioTranscriptionFinished
  ItemStreamingPartTextDelta
  ItemStreamingPartTextFinished
  ItemStreamingFunctionCallArgumentsDelta
  ItemStreamingFunctionCallArgumentsFinished
  InputSpeechStarted
  InputSpeechStopped
  InputTranscriptionFinished
  InputTranscriptionFailed
  InputAudioCommitted
  InputAudioCleared
  Error
}

enum ConversationVoice {
  Alloy
  Ash
  Ballad
  Coral
  Echo
  Sage
  Shimmer
  Verse
}

enum ComputerCallActionKind {
  Click
  DoubleClick
  Drag
  KeyPress
  Move
  Screenshot
  Scroll
  Type
  Wait
}

enum ComputerCallActionMouseButton {
  Left
  Right
  Wheel
  Back
  Forward
}

enum ComputerCallOutputStatus {
  InProgress
  Completed
  Incomplete
}

enum ComputerCallStatus {
  InProgress
  Completed
  Incomplete
}

enum ComputerToolEnvironment {
  Browser
  Mac
  Ubuntu
  Windows
}

enum FileSearchCallStatus {
  InProgress
  Searching
  Completed
  Incomplete
  Failed
}

enum FileSearchToolRanker {
  Auto
  Default20241115
}

enum FunctionCallOutputStatus {
  InProgress
  Completed
  Incomplete
}

enum FunctionCallStatus {
  InProgress
  Completed
  Incomplete
}

enum ResponseContentPartKind {
  Unknown
  InputText
  InputImage
  InputFile
  OutputText
  Refusal
}

enum ResponseImageDetailLevel {
  Auto
  High
  Low
}

enum ResponseIncompleteStatusReason {
  ContentFilter
  MaxOutputTokens
}

enum ResponseItemCollectionOrder {
  Ascending
  Descending
}

enum ResponseMessageAnnotationKind {
  FileCitation
  UriCitation
  FilePath
}

enum ResponseReasoningEffortLevel {
  High
  Low
  Medium
}

enum ResponseReasoningSummaryVerbosity {
  Concise
  Detailed
}

enum ResponseStatus {
  InProgress
  Completed
  Incomplete
  Failed
}

enum ResponseTextFormatKind {
  Unknown
  Text
  JsonObject
  JsonSchema
}

enum ResponseToolChoiceKind {
  Unknown
  Auto
  None
  Required
  Function
  FileSearch
  WebSearch
  Computer
}

enum ResponseTruncationMode {
  Auto
  Disabled
}

enum WebSearchCallStatus {
  InProgress
  Searching
  Completed
  Failed
}

enum WebSearchToolContextSize {
  High
  Low
  Medium
}

enum VectorStoreBatchFileJobStatus {
  Cancelled
  Completed
  Failed
  InProgress
}

enum VectorStoreCollectionOrder {
  Ascending
  Descending
}

enum VectorStoreExpirationAnchor {
  Unknown
  LastActiveAt
}

enum VectorStoreFileAssociationCollectionOrder {
  Ascending
  Descending
}

enum VectorStoreFileAssociationErrorCode {
  InvalidFile
  ServerError
  UnsupportedFile
}

enum VectorStoreFileAssociationStatus {
  Unknown
  InProgress
  Completed
  Cancelled
  Failed
}

enum VectorStoreFileStatusFilter {
  Cancelled
  Completed
  Failed
  InProgress
}

enum VectorStoreStatus {
  Unknown
  InProgress
  Completed
  Expired
}


enum AudioTimestampGranularities {
  Default
  Word
  Segment
}

enum AudioTranscriptionFormat {
  Simple
  Verbose
  Srt
  Vtt
  Text # Obsolete in .NET SDK, check if needed
}

enum AudioTranslationFormat {
  Simple
  Verbose
  Srt
  Vtt
  Text # Obsolete in .NET SDK, check if needed
}

enum GeneratedSpeechFormat {
  Mp3
  Opus
  Flac
  Pcm
  Wav
  Aac
}

enum GeneratedSpeechVoice {
  Alloy
  Echo
  Onyx
  Nova
  Shimmer
  Fable
  кварц
  Ash
  Coral
  Sage
  Verse # Not in .NET SDK, check if needed, maybe Ballad?
}

enum DetailLevel {
  Auto
  Low
  High
}

enum ChatReasoningEffortLevel {
  Low
  Medium
  High
}

enum ChatResponseModalities {
  Default
  Text
  Audio
}

enum ChatMessageRole {
  # Distinct from MessageRole and ConversationMessageRole
  System
  User
  Assistant
  Tool
  Function # Deprecated in favor of Tool
  Developer
}

enum ChatMessageContentPartKind {
  Text
  Refusal
  Image
  InputAudio
  File
}

enum ChatInputAudioFormat {
  Mp3
  Wav
}

enum ChatToolKind {
  Function
}

enum ChatToolCallKind {
  Function
}

enum ChatToolChoiceKind {
  Unknown
  Auto
  None
  Required
  Function
}

enum ChatOutputAudioVoice {
  Alloy
  Ash
  Ballad
  Coral
  Echo
  Sage
  Shimmer
  Verse
}

enum ChatOutputAudioFormat {
  Flac
  Mp3
  Opus
  Pcm16
  Wav
}

enum ChatFinishReason {
  Stop
  Length
  ContentFilter
  ToolCalls
  FunctionCall # Deprecated
}


# Interfaces and base classes (Same as before)
class IJsonModel {
  # Interface placeholder class
}

class IPersistableModel {
  # Interface placeholder class
}

class DataWithPagingBaseResponse {
  [Object[]] $Data
  [int] $Total
  [int] $Limit
  [string] $NextToken
}

class BaseResponse {
  [string] $Id
  [string] $Object
  [int] $Created
  [string] $Model
}

class Usage {
  [int] $PromptTokens
  [int] $CompletionTokens
  [int] $TotalTokens
}

class RunTokenUsage : Usage {
  [int] $InputTokenCount
  [int] $OutputTokenCount
  [int] $TotalTokenCount
}

class EmbeddingTokenUsage : Usage {
  [int] $InputTokenCount
  [int] $TotalTokenCount
}

class ChatTokenUsage : Usage {
  [int] $InputTokenCount
  [int] $OutputTokenCount
  [int] $TotalTokenCount
  [ChatInputTokenUsageDetails] $InputTokenDetails
  [ChatOutputTokenUsageDetails] $OutputTokenDetails
}

class OperationResult {
  # Placeholder, will improve for async later if needed
  [ContinuationToken] $RehydrationToken
  [ResponseStatus] $Status
  # [ClientResult] UpdateStatus() { return [ClientResult]::new() } # Placeholder, if implementing operations
  # [System.Threading.Tasks.ValueTask[ClientResult]] UpdateStatusAsync() { return [System.Threading.Tasks.ValueTask[ClientResult]]::new() } # Placeholder
}

class ContinuationToken {
  # Placeholder, improve for paging later if needed
  # Placeholder for ContinuationToken, needs proper implementation for paging
  [string] $Token
}

class ClientResult {
  [object] $Value
  [string] $Status # e.g., "Success", "Error"
  [hashtable] $ErrorDetails

  ClientResult([object]$value, [string]$status, [hashtable]$errorDetails) {
    $this.Value = $value
    $this.Status = $status
    $this.ErrorDetails = $errorDetails
  }

  static [ClientResult] Success([object]$value) {
    return [ClientResult]::new($value, "Success", @{})
  }

  static [ClientResult] Error([string]$message, [string]$code) {
    return [ClientResult]::new($null, "Error", @{ "Message" = $message; "Code" = $code })
  }
}


# Data Classes (Same as before, no changes requested in data classes for this step)

class RunIncompleteDetails : IJsonModel {
  [RunIncompleteReason] $Reason
}

class RunStep : IJsonModel {
  [string] $Id
  [DateTimeOffset] $CreatedAt
  [RunStatus] $Status
  [RunStepDetails] $Details
}

# Run Step Details and Outputs
class RunStepDetails : IJsonModel {
  [RunStepToolCallKind] $Kind
  [string] $CreatedMessageId
  [RunStepToolCall[]] $ToolCalls
}

class RunStepToolCall : IJsonModel {
  [string] $Id
  [RunStepToolCallKind] $Kind
  [string] $FunctionOutput
  [string] $FunctionName
  [hashtable] $FunctionArguments
  [string] $CodeInterpreterInput
  [RunStepCodeInterpreterOutput[]] $CodeInterpreterOutputs
  [FileSearchRankingOptions] $FileSearchRankingOptions
  [RunStepFileSearchResult[]] $FileSearchResults
}

class RunStepCodeInterpreterOutput : IJsonModel {
  [string] $ImageFileId
  [string] $Logs
}

class RunStepFileSearchResult : IJsonModel {
  [string] $FileId
  [string] $FileName
  [float] $Score
  [RunStepFileSearchResultContent[]] $Content
}

class RunStepFileSearchResultContent : IJsonModel {
  [RunStepFileSearchResultContentKind] $Kind
  [string] $Text
}

class RunStepTokenUsage : IJsonModel {
  [int] $InputTokenCount
  [int] $OutputTokenCount
  [int] $TotalTokenCount
}

class RunStepError : IJsonModel {
  [RunStepErrorCode] $Code
  [string] $Message
}

class ToolDefinition : IJsonModel {
  [string] $Type
  # static [CodeInterpreterToolDefinition] CreateCodeInterpreter() { } # Static factory methods will be added later if needed, for now constructor is enough
  # static [FileSearchToolDefinition] CreateFileSearch([int] $maxResults) { }
  # static [FunctionToolDefinition] CreateFunction([string] $name) { }
}

class CodeInterpreterToolDefinition : ToolDefinition {
  [CodeInterpreterToolResources] $Resources
}

class FileSearchToolDefinition : ToolDefinition {
  [int] $MaxResults
  [FileSearchRanker] $Ranker
  [FileSearchRankingOptions] $RankingOptions
}

class FunctionToolDefinition : ToolDefinition {
  [string] $FunctionName
  [string] $Description
  [hashtable] $Parameters
  [bool] $StrictParameterSchemaEnabled
}

class CodeInterpreterToolResources : IJsonModel {
  [string[]] $FileIds
}

class FileSearchToolResources : IJsonModel {
  [string[]] $VectorStoreIds
  [VectorStoreCreationHelper[]] $NewVectorStores
}


class ThreadModificationOptions : IJsonModel {
  [string] $Name
  [hashtable] $Metadata
}


class MessageContent : IJsonModel {
  [string] $Text
  [DetailLevel] $ImageDetail
}

class Message : IJsonModel {
  [MessageRole] $Role
  [MessageContent] $Content
}

class OpenAIResponse : IJsonModel {
  [string] $Id
  [string[]] $OutputText
  [RunTokenUsage] $Usage
}

class CompletionCreateRequest {
  [string] $Model = [ModelType]::GPT35Turbo
  [string[]] $Prompt
  [int] $MaxTokens = 16
  [double] $Temperature = 1.0
  [bool] $Stream = $false
  [int] $N = 1
  [string[]] $Stop
  [double] $TopP = 1.0
  [double] $FrequencyPenalty = 0.0
  [double] $PresencePenalty = 0.0
  [int] $BestOf = 1
  [hashtable] $LogitBias
}


class CreateCompletionResponse : BaseResponse {
  [string] $Id
  [string] $Model
  [Choice[]] $Choices
  [Usage] $Usage
  [long] $CreatedAtUnix
  [DateTimeOffset] $CreatedAt
}


class ModelType {
  static [string] $GPT3 = "gpt-3"
  static [string] $GPT35Turbo = "gpt-3.5-turbo"
  static [string] $GPT4 = "gpt-4"
}

class FinishReason {
  static [string] $Stop = "stop"
  static [string] $Length = "length"
  static [string] $Empty = "empty"
}

class UploadFilePurpose {
  static [string] $Assistants = "assistants"
  static [string] $Vision = "vision"
  static [string] $FineTune = "fine-tune"
}


class Choice {
  [string] $Text
  [int] $Index
  [string] $FinishReason
}



class FileCounts {
  [int] $InProgress
  [int] $Completed
  [int] $Failed
  [int] $Cancelled
  [int] $Total
}

class LogProbsResponse {
  [string[]] $Tokens
  [double[]] $TokenLogProbs
  [hashtable[]] $TopLogProbsRaw
}

class AssistantFileResponse : BaseResponse {
  [string] $Id
  [string] $AssistantId
  [long] $CreatedAtUnix
  [DateTimeOffset] $CreatedAt
}

class AssistantListResponse : DataWithPagingBaseResponse {
  [AssistantResponse[]] $Data
}

class FileListResponse : DataWithPagingBaseResponse {
  [FileResponse[]] $Data
}

class RequestCountsResponse {
  [int] $Total
  [int] $Completed
  [int] $Failed
}

class EmbeddingCreateRequest {
  [string] $Input
  [string[]] $InputAsList
  [string] $Model
  [int] $Dimensions
}

class CreateVectorStoreFileBatchRequest {
  [string[]] $FileIds
  [hashtable] $Metadata
}

class ModifyMessageRequest {
  [hashtable] $Metadata
  [string[]] $Attachments
}


class FileResponse : BaseResponse {
  [int] $Bytes
  [string] $Filename
  [string] $Purpose
  [string] $Status
  [long] $CreatedAtUnix
  [DateTimeOffset] $CreatedAt
}

class VectorStoreFileObject {
  [string] $Id
  [string] $VectorStoreId
  [string] $Status
  [FileCounts] $FileCounts
  [DateTimeOffset] $CreatedAt
}

class BatchCreateResponse {
  [string] $Id
  [string] $Endpoint
  [string] $Status
  [string] $OutputFileId
  [string] $ErrorFileId
  [long] $CreatedAtUnix
}

class SubmitToolOutputsResponse {
  [string] $Id
  [string] $Status
  [string] $Output
}

class EditService {
  [object] CreateEdit() { return $null }
}

class BetaService {
  [AssistantService] $Assistants
  [MessageService] $Messages
  [ThreadService] $Threads
  [RunService] $Runs
  [RunStepService] $RunSteps
  [VectorStores] $VectorStores
  [VectorStoreFiles] $VectorStoreFiles
}


class MessageService {
  [void] ModifyMessage([string]$id, [ModifyMessageRequest]$request) {}
}

class ThreadService {
  [object] CreateThread() { return $null }
}

class RunService {
  [object] CreateRun() { return $null }
}

class RunStepService {
  [RunStep[]] ListRunSteps() { return @([RunStep]::new()) } # Returning array for list
  [RunStep] GetRunStep() { return [RunStep]::new() }
}

# Models - Assistants API
class Assistant : BaseResponse {
  [string] $Id
  [string] $Name
  [string] $Model
  [string] $Description
  [string] $Instructions
  [string[]] $FileIds

  [hashtable] $Metadata
  [ToolDefinition[]] $Tools
  [DateTimeOffset] $CreatedAt
  [float] $NucleusSamplingFactor
  [AssistantResponseFormat] $ResponseFormat
  [float] $Temperature
}

class AssistantResponse : BaseResponse {
  [string] $Id
  [string] $Name
  [string] $Model
  [string] $Description
  [string] $Instructions
  [string[]] $FileIds
  [hashtable] $Metadata
  [ToolDefinition[]] $Tools
  [DateTimeOffset] $CreatedAt
}

class AssistantCreationOptions : IJsonModel {
  [string] $Model
  [string] $Name
  [string] $Description
  [string] $Instructions
  [hashtable] $Metadata
  [ToolDefinition[]] $Tools
  [float] $NucleusSamplingFactor
  [AssistantResponseFormat] $ResponseFormat
  [float] $Temperature
}


class AssistantResponseFormat : IJsonModel {
  [AssistantResponseFormatKind] $Kind

  static [AssistantResponseFormat] CreateJsonObjectFormat() {
    return [AssistantResponseFormat]::new() # Simple constructor for now, add properties if needed later
  }

  static [AssistantResponseFormat] CreateTextFormat() {
    return [AssistantResponseFormat]::new() # Simple constructor for now
  }
}

class AssistantDeletionResult : IJsonModel {
  [string] $AssistantId
  [bool] $Deleted
}

class AssistantThread : BaseResponse {
  [string] $Id
  [hashtable] $Metadata
  [DateTimeOffset] $CreatedAt
}

class ThreadDeletionResult : IJsonModel {
  [string] $ThreadId
  [bool] $Deleted
}


class ThreadRun : BaseResponse {
  [string] $Id
  [string] $Model
  [string] $AssistantId
  [string] $ThreadId
  [RunStatus] $Status
  [RunError] $LastError
  [RunTokenUsage] $Usage
  [RunIncompleteDetails] $IncompleteDetails
  [DateTimeOffset] $StartedAt
  [DateTimeOffset] $ExpiresAt
  [DateTimeOffset] $CancelledAt
  [DateTimeOffset] $FailedAt
  [DateTimeOffset] $CompletedAt
  [string] $Instructions
  [ToolDefinition[]] $Tools
  [hashtable] $Metadata
  [float] $Temperature
  [float] $NucleusSamplingFactor
  [AssistantResponseFormat] $ResponseFormat
  [int] $MaxInputTokenCount
  [int] $MaxOutputTokenCount
  [bool] $AllowParallelToolCalls
  [DateTimeOffset] $CreatedAt
  [RunTruncationStrategy] $TruncationStrategy
  [ToolConstraint] $ToolConstraint
}

class RunCreationOptions : IJsonModel {
  [string] $AssistantId
  [string] $ModelOverride
  [string] $InstructionsOverride
  [ToolDefinition[]] $ToolsOverride
  [hashtable] $Metadata
  [float] $Temperature
  [float] $NucleusSamplingFactor
  [AssistantResponseFormat] $ResponseFormat
  [int] $MaxInputTokenCount
  [int] $MaxOutputTokenCount
  [bool] $AllowParallelToolCalls
  [RunTruncationStrategy] $TruncationStrategy
  [ToolConstraint] $ToolConstraint
  [string] $AdditionalInstructions
  [ThreadInitializationMessage[]] $AdditionalMessages
}

class RunModificationOptions : IJsonModel {
  [hashtable] $Metadata
}

class RunError : IJsonModel {
  [RunErrorCode] $Code
  [string] $Message
}


class RunTruncationStrategy : IJsonModel {
  [string] $StrategyType # could be enum but dotnet code uses string
  static [RunTruncationStrategy] CreateLastMessagesStrategy([int] $lastMessageCount) {
    return [RunTruncationStrategy]::new() # Simple constructor for now, add properties if needed later
  }
}

class ToolConstraint : IJsonModel {
  [string] $ConstraintType # could be enum but dotnet code uses string
  [ToolDefinition] $ToolDefinition
}

class ThreadInitializationMessage : IJsonModel {
  [MessageRole] $Role
  [MessageContent[]] $Content
  [MessageCreationOptions] $Options
}

class MessageCreationOptions : IJsonModel {
  [hashtable] $Metadata
  [MessageCreationAttachment[]] $Attachments
}

class MessageCreationAttachment : IJsonModel {
  [string] $FileId
  [ToolDefinition[]] $Tools
}

class ThreadMessage : BaseResponse {
  [string] $Id
  [string] $ThreadId
  [MessageRole] $Role
  [MessageContent[]] $Content
  [hashtable] $Metadata
  [string] $AssistantId
  [string] $RunId
  [MessageStatus] $Status
  [MessageFailureDetails] $IncompleteDetails
  [DateTimeOffset] $CompletedAt
  [DateTimeOffset] $CreatedAt
  [DateTimeOffset] $IncompleteAt
  [MessageCreationAttachment[]] $Attachments
}

class MessageFailureDetails : IJsonModel {
  [MessageFailureReason] $Reason
}

class MessageModificationOptions : IJsonModel {
  [hashtable] $Metadata
}

class MessageDeletionResult : IJsonModel {
  [string] $MessageId
  [bool] $Deleted
}

class FileSearchRankingOptions : IJsonModel {
  [FileSearchRanker] $Ranker
  [float] $ScoreThreshold
}

class VectorStoreCreationHelper : IJsonModel {
  [string[]] $FileIds
  [OpenAIFile[]] $Files
  [hashtable] $Metadata
  [FileChunkingStrategy] $ChunkingStrategy
}

class FileChunkingStrategy : IJsonModel {
  [string] $StrategyType # could be enum, but dotnet code uses string
  [int] $MaxTokensPerChunk
  [int] $OverlappingTokenCount
}


# OpenAI File Management (Reused and expanded from existing)
class OpenAIFile : BaseResponse {
  [string] $Id
  [int] $Bytes
  [string] $Filename
  [FilePurpose] $Purpose
  [string] $Status # FileStatus Enum
  [string] $StatusDetails
  [DateTimeOffset] $CreatedAt
  [DateTimeOffset] $ExpiresAt
}

class OpenAIFileCollection : DataWithPagingBaseResponse {
  [OpenAIFile[]] $Files
}

class FileDeletionResult : IJsonModel {
  [string] $FileId
  [bool] $Deleted
}


class EmbeddingCreateResponse : BaseResponse {
  [OpenAIEmbedding[]] $Data
  [Usage] $Usage
}

class OpenAIEmbedding : IJsonModel {
  [int] $Index
  [double[]] $Vector
}

class OpenAIEmbeddingCollection : DataWithPagingBaseResponse {
  [OpenAIEmbedding[]] $Data
  [string] $Model
  [EmbeddingTokenUsage] $Usage
}

# Chat API Specific Classes
class ChatCompletionCreateRequest {
  [string] $Model
  [ChatMessage[]] $Messages
  [double] $Temperature = 1.0
  [int] $MaxTokens
  [int] $N = 1
  [string[]] $Stop
  [double] $TopP = 1.0
  [double] $FrequencyPenalty = 0.0
  [double] $PresencePenalty = 0.0
  [string] $User
  [ChatTool[]] $Tools
  [ChatToolChoice] $ToolChoice
  [ChatResponseFormat] $ResponseFormat
  [int] $TopLogProbabilityCount
  [bool] $IncludeLogProbabilities
  [hashtable] $LogitBiases
  [string] $EndUserId
  [bool] $AllowParallelToolCalls
  [ChatReasoningEffortLevel] $ReasoningEffortLevel
  [ChatResponseModalities] $ResponseModalities
  [ChatAudioOptions] $AudioOptions
  [ChatWebSearchOptions] $WebSearchOptions
  [long] $Seed
  [bool] $StoredOutputEnabled
  [hashtable] $Metadata
  [ChatOutputPrediction] $OutputPrediction
}

class ChatMessage : IJsonModel {
  [ChatMessageRole] $Role
  [ChatMessageContent[]] $Content
  [string] $ParticipantName
  [ChatToolCall[]] $ToolCalls
  [ChatFunctionCall] $FunctionCall # Obsolete, kept for compatibility?
  [string] $Refusal
  [ChatOutputAudioReference] $OutputAudioReference
}

class ChatMessageContent : IJsonModel {
  [ChatMessageContentPart[]] $Parts
}

class ChatMessageContentPart : IJsonModel {
  [ChatMessageContentPartKind] $Kind
  [string] $Text
  [string] $Refusal
  [Uri] $ImageUri
  [MessageImageDetail] $ImageDetailLevel
  [byte[]] $ImageBytes
  [string] $ImageBytesMediaType
  [byte[]] $InputAudioBytes
  [ChatInputAudioFormat] $InputAudioFormat
  [string] $FileId
  [string] $Filename
  [byte[]] $FileBytes
  [string] $FileBytesMediaType
}

class ChatTool : IJsonModel {
  [ChatToolKind] $Kind
  [string] $FunctionName
  [string] $FunctionDescription
  [hashtable] $FunctionParameters
  [bool] $FunctionSchemaIsStrict
}

class ChatToolCall : IJsonModel {
  [ChatToolCallKind] $Kind
  [string] $Id
  [string] $FunctionName
  [hashtable] $FunctionArguments
}

class ChatFunctionCall : IJsonModel {
  # Obsolete, kept for compatibility?
  [string] $FunctionName
  [hashtable] $FunctionArguments
}

class ChatToolChoice : IJsonModel {
  [ChatToolChoiceKind] $Kind
  [string] $FunctionName
}

class ChatResponseFormat : IJsonModel {
  [ResponseTextFormatKind] $Kind
  [hashtable] $JsonSchema
  [string] $JsonSchemaFormatName
  [string] $JsonSchemaFormatDescription
  [bool] $JsonSchemaIsStrict
}

class ChatAudioOptions : IJsonModel {
  [ChatOutputAudioVoice] $OutputAudioVoice
  [ChatOutputAudioFormat] $OutputAudioFormat
}

class ChatWebSearchOptions : IJsonModel {
  # Placeholder, no properties in .NET SDK
}

class ChatOutputPrediction : IJsonModel {
  [ChatMessageContent[]] $StaticContentParts
}

class ChatOutputAudio : IJsonModel {
  [string] $Id
  [byte[]] $AudioBytes
  [string] $Transcript
  [DateTimeOffset] $ExpiresAt
}

class ChatOutputAudioReference : IJsonModel {
  [string] $Id
}

class ChatTokenLogProbabilityDetails : IJsonModel {
  [string] $Token
  [double] $LogProbability
  [byte[]] $Utf8Bytes
  [ChatTokenTopLogProbabilityDetails[]] $TopLogProbabilities
}

class ChatTokenTopLogProbabilityDetails : IJsonModel {
  [string] $Token
  [double] $LogProbability
  [byte[]] $Utf8Bytes
}

class StreamingChatCompletionUpdate : IJsonModel {
  [string] $CompletionId
  [ChatMessageContent] $ContentUpdate
  [StreamingChatFunctionCallUpdate] $FunctionCallUpdate # Obsolete, compatibility?
  [StreamingChatToolCallUpdate[]] $ToolCallUpdates
  [ChatMessageRole] $Role
  [string] $RefusalUpdate
  [ChatTokenLogProbabilityDetails[]] $ContentTokenLogProbabilities
  [ChatTokenLogProbabilityDetails[]] $RefusalTokenLogProbabilities
  [ChatFinishReason] $FinishReason
  [DateTimeOffset] $CreatedAt
  [string] $Model
  [string] $SystemFingerprint
  [ChatTokenUsage] $Usage
  [StreamingChatOutputAudioUpdate] $OutputAudioUpdate
}

class StreamingChatFunctionCallUpdate : IJsonModel {
  # Obsolete, compatibility?
  [string] $FunctionName
  [hashtable] $FunctionArgumentsUpdate # BinaryData in .NET
}

class StreamingChatToolCallUpdate : IJsonModel {
  [int] $Index
  [string] $ToolCallId
  [ChatToolCallKind] $Kind
  [string] $FunctionName
  [hashtable] $FunctionArgumentsUpdate # BinaryData in .NET
}

class StreamingChatOutputAudioUpdate : IJsonModel {
  [string] $Id
  [DateTimeOffset] $ExpiresAt
  [string] $TranscriptUpdate
  [byte[]] $AudioBytesUpdate
}

class ChatCompletion : BaseResponse {
  [string] $Id
  [string] $Model
  [int] $PromptTokens
  [int] $CompletionTokens
  [int] $TotalTokens

  [ChatMessageContent] $Content
  [ChatMessageAnnotation[]] $Annotations
  [ChatFinishReason] $FinishReason
  [ChatFunctionCall] $FunctionCall # Obsolete, compatibility?
  [ChatToolCall[]] $ToolCalls
  [string] $Refusal
  [ChatOutputAudio] $OutputAudio
  [DateTimeOffset] $CreatedAt
  [ChatTokenLogProbabilityDetails[]] $ContentTokenLogProbabilities
  [ChatTokenLogProbabilityDetails[]] $RefusalTokenLogProbabilities
  [ChatMessageRole] $Role
  [string] $SystemFingerprint
  [ChatTokenUsage] $Usage
}

class ChatInputTokenUsageDetails : IJsonModel {
  [int] $AudioTokenCount
  [int] $CachedTokenCount
  [int] $TextTokenCount
}

class ChatOutputTokenUsageDetails : IJsonModel {
  [int] $ReasoningTokenCount
  [int] $AudioTokenCount
  [int] $AcceptedPredictionTokenCount
  [int] $RejectedPredictionTokenCount
}

class ChatMessageAnnotation : IJsonModel {
  [int] $EndIndex
  [int] $StartIndex
  [string] $WebResourceTitle
  [Uri] $WebResourceUri
}

class ThreadCreationOptions : IJsonModel {
  [ThreadInitializationMessage[]] $InitialMessages
  [hashtable] $Metadata
  [ToolResources] $ToolResources
}

class ToolResources : IJsonModel {
  [CodeInterpreterToolResources] $CodeInterpreter
  [FileSearchToolResources] $FileSearch
}

class OpenAIConfig {
  [securestring] $ApiKey
  [string] $BaseUrl = "https://api.openai.com/v1"
  [string] $Organization
  [int] $MaxRetries = 3
  [int] $RetryDelay = 1000
  OpenAIConfig() {}
}

# Moderation API Classes
class CreateModerationRequest {
  [string] $Input
  [string[]] $InputAsList
  [string] $Model
}

class ModerationCategory {
  [bool] $Flagged
  [float] $Score
}

class ModerationResult : IJsonModel {
  [bool] $Flagged
  [ModerationCategory] $Hate
  [ModerationCategory] $HateThreatening
  [ModerationCategory] $Harassment
  [ModerationCategory] $HarassmentThreatening
  [ModerationCategory] $SelfHarm
  [ModerationCategory] $SelfHarmIntent
  [ModerationCategory] $SelfHarmInstructions
  [ModerationCategory] $Sexual
  [ModerationCategory] $SexualMinors
  [ModerationCategory] $Violence
  [ModerationCategory] $ViolenceGraphic
  [ModerationCategory] $Illicit
  [ModerationCategory] $IllicitViolent
}

class ModerationResultCollection : DataWithPagingBaseResponse {
  [ModerationResult[]] $Data
  [string] $Id
  [string] $Model
}

# Image API Classes
class ImageGenerationOptions : IJsonModel {
  [int] $ImageCount
  [GeneratedImageSize] $Size
  [GeneratedImageFormat] $ResponseFormat
  [GeneratedImageQuality] $Quality
  [GeneratedImageStyle] $Style
  [string] $EndUserId
}

class ImageEditOptions : ImageGenerationOptions {
  # Inherits options from ImageGenerationOptions and adds specific ones if needed
}

class ImageVariationOptions : ImageGenerationOptions {
  # Inherits options from ImageGenerationOptions and adds specific ones if needed
}

enum GeneratedImageSize {
  W256xH256
  W512xH512
  W1024xH1024
  W1024xH1792
  W1792xH1024
}

class GeneratedImage {
  [byte[]] $ImageBytes
  [Uri] $ImageUri
  [string] $RevisedPrompt
}

class GeneratedImageCollection : DataWithPagingBaseResponse {
  [GeneratedImage[]] $Data
  [DateTimeOffset] $CreatedAt
}

# Model API Classes
class OpenAIModel : BaseResponse {
  [string] $Id
  [string] $OwnedBy
  [DateTimeOffset] $CreatedAt
}

class OpenAIModelCollection : DataWithPagingBaseResponse {
  [OpenAIModel[]] $Data
}

class ModelDeletionResult : IJsonModel {
  [string] $ModelId
  [bool] $Deleted
}

# Audio API Classes
class AudioTranscriptionOptions : IJsonModel {
  [string] $Language
  [string] $Prompt
  [AudioTranscriptionFormat] $ResponseFormat
  [float] $Temperature
  [AudioTimestampGranularities] $TimestampGranularities
}

class AudioTranslationOptions : IJsonModel {
  [string] $Prompt
  [AudioTranslationFormat] $ResponseFormat
  [float] $Temperature
}

class SpeechGenerationOptions : IJsonModel {
  [GeneratedSpeechFormat] $ResponseFormat
  [float] $SpeedRatio
}

class AudioTranscription : IJsonModel {
  [string] $Text
  [string] $Language
  [TimeSpan] $Duration
  [TranscribedSegment[]] $Segments
  [TranscribedWord[]] $Words
}

class AudioTranslation : IJsonModel {
  [string] $Text
  [string] $Language
  [TimeSpan] $Duration
  [TranscribedSegment[]] $Segments
}

class TranscribedSegment : IJsonModel {
  [int] $Id
  [int] $SeekOffset
  [TimeSpan] $StartTime
  [TimeSpan] $EndTime
  [string] $Text
  [int[]] $TokenIds # ReadOnlyMemory<int> in .NET, convert to int[]
  [float] $Temperature
  [float] $AverageLogProbability
  [float] $CompressionRatio
  [float] $NoSpeechProbability
}

class TranscribedWord : IJsonModel {
  [string] $Word
  [TimeSpan] $StartTime
  [TimeSpan] $EndTime
}

# Vector Store API Classes
class VectorStoreCreationOptions : IJsonModel {
  [string] $Name
  [hashtable] $Metadata
  [FileChunkingStrategy] $ChunkingStrategy
  [VectorStoreExpirationPolicy] $ExpirationPolicy
  [string[]] $FileIds
}

class VectorStoreModificationOptions : IJsonModel {
  [string] $Name
  [hashtable] $Metadata
  [VectorStoreExpirationPolicy] $ExpirationPolicy
}

class VectorStoreExpirationPolicy : IJsonModel {
  [VectorStoreExpirationAnchor] $Anchor
  [int] $Days
}

class VectorStore : BaseResponse {
  [string] $Id
  [string] $Name
  [int] $UsageBytes
  [hashtable] $Metadata
  [VectorStoreStatus] $Status
  [DateTimeOffset] $CreatedAt
  [DateTimeOffset] $LastActiveAt
  [DateTimeOffset] $ExpiresAt
  [VectorStoreFileCounts] $FileCounts
  [VectorStoreExpirationPolicy] $ExpirationPolicy
}

class VectorStoreListResponse : DataWithPagingBaseResponse {
  [VectorStore[]] $Data
}

class VectorStoreDeletionResult : IJsonModel {
  [string] $VectorStoreId
  [bool] $Deleted
}

class VectorStoreFileAssociation : IJsonModel {
  [string] $VectorStoreId
  [string] $FileId
  [VectorStoreFileAssociationStatus] $Status
  [VectorStoreFileAssociationError] $LastError
  [DateTimeOffset] $CreatedAt
  [int] $Size
  [FileChunkingStrategy] $ChunkingStrategy
  [hashtable] $Attributes # IDictionary<string, BinaryData> in .NET, using hashtable for simplicity
}

class VectorStoreFileAssociationListResponse : DataWithPagingBaseResponse {
  [VectorStoreFileAssociation[]] $Data
}

class VectorStoreFileAssociationError : IJsonModel {
  [VectorStoreFileAssociationErrorCode] $Code
  [string] $Message
}

class FileFromStoreRemovalResult : IJsonModel {
  [string] $FileId
  [bool] $Removed
}

class VectorStoreFileCounts : IJsonModel {
  [int] $InProgress
  [int] $Completed
  [int] $Failed
  [int] $Cancelled
  [int] $Total
}

class VectorStoreBatchFileJob : BaseResponse {
  [string] $Id
  [string] $VectorStoreId
  [DateTimeOffset] $CreatedAt
  [VectorStoreBatchFileJobStatus] $Status
  [VectorStoreFileCounts] $FileCounts
}

class VectorStoreBatchFileJobResponse : VectorStoreBatchFileJob {
  # To avoid name collision in list response
}

class VectorStoreBatchFileJobListResponse : DataWithPagingBaseResponse {
  [VectorStoreBatchFileJobResponse[]] $Data
}


# Service classes (Partial - expand as needed, focusing on structure)
class ModelService {
  hidden [OpenAIConfig]$Configuration

  ModelService([OpenAIConfig]$config) {
    $this.Configuration = $config
  }
  [ClientResult] GetModels() { return [ClientResult]::Error("Not Implemented", "NotImplemented") } # Example using ClientResult error
  [ClientResult] DeleteModel() { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] GetModel() { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
}

class CompletionService {
  hidden [OpenAIConfig]$Configuration

  CompletionService([OpenAIConfig]$config) {
    $this.Configuration = $config
  }
  [ClientResult] CreateCompletion([CompletionCreateRequest]$request) {
    $uri = "$($this.Configuration.BaseUrl)/completions"
    $headers = @{
      "Authorization" = "Bearer $($this.Configuration.ApiKey)"
      "Content-Type"  = "application/json"
    }

    try {
      $body = $request | ConvertTo-Json -Depth 10
      $response = Invoke-RestMethod -Uri $uri -Method Post -Headers $headers -Body $body -ErrorAction Stop

      $resp = [CreateCompletionResponse]::new()
      $resp.Id = $response.id
      $resp.Object = $response.object
      $resp.Created = $response.created
      $resp.Model = $response.model
      $resp.CreatedAtUnix = $response.created
      $resp.CreatedAt = [DateTimeOffset]::FromUnixTimeSeconds($response.created)

      # Process choices
      $choices = @()
      foreach ($choice in $response.choices) {
        $c = [Choice]::new()
        $c.Text = $choice.text
        $c.Index = $choice.index
        $c.FinishReason = $choice.finish_reason
        $choices += $c
      }
      $resp.Choices = $choices

      # Process usage
      $usage = [Usage]::new()
      $usage.PromptTokens = $response.usage.prompt_tokens
      $usage.CompletionTokens = $response.usage.completion_tokens
      $usage.TotalTokens = $response.usage.total_tokens
      $resp.Usage = $usage

      return [ClientResult]::Success($resp)
    } catch {
      return [ClientResult]::Error($_.Exception.Message, $_.Exception.GetType().FullName)
    }
  }
}

class EmbeddingService {
  hidden [OpenAIConfig]$Configuration

  EmbeddingService([OpenAIConfig]$config) {
    $this.Configuration = $config
  }
  [ClientResult] CreateEmbedding([EmbeddingCreateRequest]$request) {
    $uri = "$($this.Configuration.BaseUrl)/embeddings"
    $headers = @{
      "Authorization" = "Bearer $($this.Configuration.ApiKey)"
      "Content-Type"  = "application/json"
    }

    try {
      $body = $request | ConvertTo-Json -Depth 10
      $response = Invoke-RestMethod -Uri $uri -Method Post -Headers $headers -Body $body -ErrorAction Stop

      $resp = [EmbeddingCreateResponse]::new()
      $resp.Id = $response.id
      $resp.Object = $response.object
      $resp.Created = $response.created
      $resp.Model = $response.model
      $resp.Data = $response.data
      $resp.Usage = [Usage]::new($response.usage)

      return [ClientResult]::Success($resp)
    } catch {
      return [ClientResult]::Error($_.Exception.Message, $_.Exception.GetType().FullName)
    }
  }
}

class FileService {
  hidden [OpenAIConfig]$Configuration
  FileService([OpenAIConfig]$config) {
    $this.Configuration = $config
  }
  [ClientResult] ListFiles() { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] UploadFile() { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] DeleteFile([string]$fileId) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] GetFile([string]$fileId) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] DownloadFile([string]$fileId) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
}

class FineTuneService {
  hidden [OpenAIConfig]$Configuration

  FineTuneService([OpenAIConfig]$config) {
    $this.Configuration = $config
  }
  [ClientResult] CreateFineTune() { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
}

class ModerationService {
  hidden [OpenAIConfig]$Configuration

  ModerationService([OpenAIConfig]$config) {
    $this.Configuration = $config
  }
  [ClientResult] CreateModeration([CreateModerationRequest]$request) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
}

class ImageService {
  hidden [OpenAIConfig]$Configuration

  ImageService([OpenAIConfig]$config) {
    $this.Configuration = $config
  }
  [ClientResult] CreateImage([ImageGenerationOptions]$request) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] CreateImageEdit([ImageEditOptions]$request) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] CreateImageVariation([ImageVariationOptions]$request) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
}

class AudioService {
  hidden [OpenAIConfig]$Configuration

  AudioService([OpenAIConfig]$config) {
    $this.Configuration = $config
  }
  [ClientResult] TranscribeAudio() { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] TranslateAudio() { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] GenerateSpeech() { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
}

class AssistantModificationOptions : IJsonModel {
  [string] $Model
  [string] $Name
  [string] $Description
  [string] $Instructions
  [hashtable] $Metadata
  [ToolDefinition[]] $Tools
  [float] $NucleusSamplingFactor
  [AssistantResponseFormat] $ResponseFormat
  [float] $Temperature
}

class AssistantService {
  hidden [OpenAIConfig]$Configuration

  AssistantService([OpenAIConfig]$config) {
    $this.Configuration = $config
  }
  [ClientResult] ListAssistants([AssistantCollectionOptions]$options) { return [ClientResult]::Error("Not Implemented", "NotImplemented") } # Using Options class
  [ClientResult] GetAssistant([string]$assistantId) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] CreateAssistant([AssistantCreationOptions]$request) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] ModifyAssistant([string]$assistantId, [AssistantModificationOptions]$request) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] DeleteAssistant([string]$assistantId) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] CreateThreadAndRun() { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] CreateRun() { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] CancelRun([string]$threadId, [string]$runId) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] GetRun([string]$threadId, [string]$runId) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] CreateThread([ThreadCreationOptions]$request) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] GetThread([string]$threadId) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] DeleteThread([string]$threadId) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] ModifyThread([string]$threadId, [ThreadModificationOptions]$request) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] CreateMessage([string]$threadId, [MessageRole]$role, [MessageContent[]]$content, [MessageCreationOptions]$options) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] GetMessage([string]$threadId, [string]$messageId) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] ModifyMessage([string]$threadId, [string]$messageId, [MessageModificationOptions]$request) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] DeleteMessage([string]$threadId, [string]$messageId) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
}

class VectorStores {
  hidden [OpenAIConfig]$Configuration

  VectorStores([OpenAIConfig]$config) {
    $this.Configuration = $config
  }
  [ClientResult] ListVectorStores([VectorStoreCollectionOptions]$options) { return [ClientResult]::Error("Not Implemented", "NotImplemented") } # Using Options class
  [ClientResult] CreateVectorStore([VectorStoreCreationOptions]$request) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] GetVectorStore([string]$vectorStoreId) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] ModifyVectorStore([string]$vectorStoreId, [VectorStoreModificationOptions]$request) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] DeleteVectorStore([string]$vectorStoreId) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] GetFileAssociations([string]$vectorStoreId) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] GetFileAssociation([string]$vectorStoreId, [string]$fileId) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] RemoveFileFromStore([string]$vectorStoreId, [string]$fileId) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] CreateBatchFileJob([string]$vectorStoreId, [CreateVectorStoreFileBatchRequest]$request) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] GetBatchFileJob([string]$vectorStoreId, [string]$batchId) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] CancelBatchFileJob([string]$vectorStoreId, [string]$batchId) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
  [ClientResult] AddFileToVectorStore([string]$vectorStoreId, [string]$fileId) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
}

class VectorStoreFiles {
  hidden [OpenAIConfig]$Configuration

  VectorStoreFiles([OpenAIConfig]$config) {
    $this.Configuration = $config
  }
  [ClientResult] CreateVectorStoreFile([string]$vectorStoreId, [object]$request) { return [ClientResult]::Error("Not Implemented", "NotImplemented") }
}

class ChatService {
  hidden [OpenAIConfig]$Configuration

  ChatService([OpenAIConfig]$config) {
    $this.Configuration = $config
  }

  [ClientResult] CreateChatCompletion([ChatCompletionCreateRequest]$request) {
    $uri = "$($this.Configuration.BaseUrl)/chat/completions"
    $headers = @{
      "Authorization" = "Bearer $($this.Configuration.ApiKey)"
      "Content-Type"  = "application/json"
    }

    try {
      $body = $request | ConvertTo-Json -Depth 10
      $response = Invoke-RestMethod -Uri $uri -Method Post -Headers $headers -Body $body -ErrorAction Stop

      $chatResp = [ChatCompletion]::new()
      $chatResp.Id = $response.id
      $chatResp.Object = $response.object
      $chatResp.Created = [DateTimeOffset]::FromUnixTimeSeconds($response.created)
      $chatResp.Model = $response.model
      $chatResp.SystemFingerprint = $response.system_fingerprint
      $chatResp.FinishReason = [ChatFinishReason]::Parse([ChatFinishReason], $response.choices[0].finish_reason, $true) # Assuming only one choice for now
      $chatResp.Role = [ChatMessageRole]::Parse([ChatMessageRole], $response.choices[0].message.role, $true)

      # Process Content
      $contentParts = @()
      if ($response.choices[0].message.content) {
        $textPart = [ChatMessageContentPart]::CreateTextPart($response.choices[0].message.content)
        $contentParts += $textPart
      }
      $chatResp.Content = [ChatMessageContent]::new($contentParts)


      # Process Usage
      $usage = [ChatTokenUsage]::new()
      $usage.PromptTokens = $response.usage.prompt_tokens
      $usage.CompletionTokens = $response.usage.completion_tokens
      $usage.TotalTokens = $response.usage.total_tokens
      $chatResp.Usage = $usage

      return [ClientResult]::Success($chatResp)
    } catch {
      return [ClientResult]::Error($_.Exception.Message, $_.Exception.GetType().FullName)
    }
  }
}


class AddFileToVectorStoreOperation : OperationResult {
  [string] $VectorStoreId
  [string] $FileId
  [VectorStoreFileAssociationStatus] $Status
  [VectorStoreFileAssociation] $Value
  [ContinuationToken] $RehydrationToken
}

class CreateBatchFileJobOperation : OperationResult {
  [string] $VectorStoreId
  [string] $BatchId
  [VectorStoreBatchFileJobStatus] $Status
  [VectorStoreBatchFileJob] $Value
  [ContinuationToken] $RehydrationToken
}

class CreateVectorStoreOperation : OperationResult {
  [string] $VectorStoreId
  [VectorStoreStatus] $Status
  [VectorStore] $Value
  [ContinuationToken] $RehydrationToken
}


# Options Classes

class AssistantCollectionOptions {
  [AssistantCollectionOrder] $Order
  [int] $PageSizeLimit
  [string] $AfterId
  [string] $BeforeId
}

class VectorStoreCollectionOptions {
  [VectorStoreCollectionOrder] $Order
  [int] $PageSizeLimit
  [string] $AfterId
  [string] $BeforeId
}


<#
.SYNOPSIS
Main OpenAI Client Class
.EXAMPLE
$config = [OpenAIConfig]@{
    ApiKey = "sk-1234567890"
}

$openaiClient = [OpenAIClient]::new($config)

$request = [CompletionCreateRequest]@{
  Model = [ModelType]::GPT35Turbo
  Prompt = "Hello, how are you today?"
  MaxTokens = 50
}

$responseResult = $openaiClient.Completions.CreateCompletion($request)

if ($responseResult.Status -eq "Success") {
    $response = $responseResult.Value
    # Get completion text
    $response.Choices.Text
} else {
    Write-Error "API Error: $($responseResult.ErrorDetails.Message) - Code: $($responseResult.ErrorDetails.Code)"
}
#>

class OpenAIClient {
  [OpenAIConfig]$Configuration
  [CompletionService] $Completions
  [EmbeddingService] $Embeddings
  [FileService] $Files
  [FineTuneService] $FineTunes
  [ModerationService] $Moderations
  [ImageService] $Images
  [AudioService] $Audio
  [AssistantService] $Assistants
  [VectorStores] $VectorStores
  [VectorStoreFiles] $VectorStoreFiles
  [ChatService] $Chats  # Add ChatService
  [ModelService] $Models


  OpenAIClient([OpenAIConfig]$config) {
    # Renamed constructor
    # todo: $this.PsObject.Properties.Add([psscriptproperty]::new("Configuration", { return $config }, { throw [setvalueException]::new("Configuration is read only") }))
    $this.Configuration = $config
    $this.Completions = [CompletionService]::new($config)
    $this.Embeddings = [EmbeddingService]::new($config)
    $this.Files = [FileService]::new($config)
    $this.FineTunes = [FineTuneService]::new($config)
    $this.Moderations = [ModerationService]::new($config)
    $this.Images = [ImageService]::new($config)
    $this.Audio = [AudioService]::new($config)
    $this.Assistants = [AssistantService]::new($config)
    $this.VectorStores = [VectorStores]::new($config)
    $this.VectorStoreFiles = [VectorStoreFiles]::new($config)
    $this.Chats = [ChatService]::new($config) # Initialize ChatService
    $this.Models = [ModelService]::new($config)
  }
}

#endregion Classes

# Types that will be available to users when they import the module.
$typesToExport = @(
  [OpenAIClient]
  [OpenAIConfig]
  [ClientResult]

  [BaseResponse]

  [Usage]
  [ModelType]
  [FinishReason]
  [UploadFilePurpose]
  [FilePurpose]
  [FileStatus]
  [DetailLevel]
  [GeneratedImageFormat]
  [GeneratedImageQuality]
  [GeneratedImageStyle]
  [AudioTranscriptionFormat]
  [AudioTranslationFormat]
  [GeneratedSpeechFormat]
  [GeneratedSpeechVoice]
  [DataWithPagingBaseResponse]
  [ChatReasoningEffortLevel]
  [ChatResponseModalities]
  [ChatMessageRole]
  [ChatMessageContentPartKind]
  [ChatInputAudioFormat]
  [ChatToolKind]
  [ChatToolCallKind]
  [ChatToolChoiceKind]
  [ChatOutputAudioVoice]
  [ChatOutputAudioFormat]
  [ChatFinishReason]
  [AssistantCollectionOrder]
  [AssistantResponseFormatKind]
  [FileSearchRanker]
  [MessageCollectionOrder]
  [MessageRole]
  [MessageStatus]
  [MessageFailureReason]
  [MessageImageDetail]
  [RunCollectionOrder]
  [RunErrorCode]
  [RunIncompleteReason]
  [RunStatus]
  [RunStepCollectionOrder]
  [RunStepErrorCode]
  [RunStepFileSearchResultContentKind]
  [RunStepKind]
  [RunStepStatus]
  [RunStepToolCallKind]
  [StreamingUpdateReason]
  [VectorStoreBatchFileJobStatus]
  [VectorStoreCollectionOrder]
  [VectorStoreExpirationAnchor]
  [VectorStoreFileAssociationCollectionOrder]
  [VectorStoreFileAssociationErrorCode]
  [VectorStoreFileAssociationStatus]
  [VectorStoreFileStatusFilter]
  [VectorStoreStatus]
  [AudioTimestampGranularities]
  [ConversationAudioFormat]
  [ConversationContentModalities]
  [ConversationContentPartKind]
  [ConversationIncompleteReason]
  [ConversationItemStatus]
  [ConversationMessageRole]
  [ConversationStatus]
  [ConversationToolChoiceKind]
  [ConversationToolKind]
  [ConversationTranscriptionModel]
  [ConversationTurnDetectionKind]
  [ConversationUpdateKind]
  [ConversationVoice]
  [ComputerCallActionKind]
  [ComputerCallActionMouseButton]
  [ComputerCallOutputStatus]
  [ComputerCallStatus]
  [ComputerToolEnvironment]
  [FileSearchCallStatus]
  [FileSearchToolRanker]
  [FunctionCallOutputStatus]
  [FunctionCallStatus]
  [ResponseContentPartKind]
  [ResponseImageDetailLevel]
  [ResponseIncompleteStatusReason]
  [ResponseItemCollectionOrder]
  [ResponseMessageAnnotationKind]
  [ResponseReasoningEffortLevel]
  [ResponseReasoningSummaryVerbosity]
  [ResponseStatus]
  [ResponseTextFormatKind]
  [ResponseToolChoiceKind]
  [ResponseTruncationMode]
  [WebSearchCallStatus]
  [WebSearchToolContextSize]
  [GeneratedImageSize]

  [CompletionCreateRequest]
  [EmbeddingCreateRequest]
  [CreateModerationRequest]
  [ImageGenerationOptions]
  [ImageEditOptions]
  [ImageVariationOptions]
  [AudioTranscriptionOptions]
  [AudioTranslationOptions]
  [SpeechGenerationOptions]
  [AssistantCreationOptions]
  [RunCreationOptions]
  [ThreadCreationOptions]
  [CreateVectorStoreFileBatchRequest]
  [ChatCompletionCreateRequest]
  [VectorStoreCreationOptions]
  [VectorStoreModificationOptions]
  [AssistantModificationOptions]
  [ThreadModificationOptions]
  [MessageModificationOptions]
  [ModifyMessageRequest]

  [CreateCompletionResponse]
  [EmbeddingCreateResponse]
  [ModerationResultCollection]
  [GeneratedImageCollection]
  [AudioTranscription]
  [AudioTranslation]
  [AssistantListResponse]
  [AssistantResponse]
  [ThreadRun]
  [AssistantThread]
  [ThreadMessage]
  [FileListResponse]
  [FileResponse]
  [VectorStoreListResponse]
  [VectorStore]
  [VectorStoreFileAssociationListResponse]
  [VectorStoreFileAssociation]
  [VectorStoreBatchFileJobListResponse]
  [VectorStoreBatchFileJobResponse]
  [ChatCompletion]
  [OpenAIModelCollection]
  [OpenAIModel]
  [AssistantDeletionResult]
  [ThreadDeletionResult]
  [MessageDeletionResult]
  [FileDeletionResult]
  [VectorStoreDeletionResult]
  [ModelDeletionResult]

  [AssistantCollectionOptions]
  [VectorStoreCollectionOptions]
  [AssistantResponseFormat]
  [RunTruncationStrategy]
  [ChatResponseFormat]
  [AssistantModificationOptions]
  [ThreadModificationOptions]
  [MessageModificationOptions]
  [VectorStoreModificationOptions]
)
$TypeAcceleratorsClass = [PsObject].Assembly.GetType('System.Management.Automation.TypeAccelerators')
foreach ($Type in $typestoExport) {
  if ($Type.FullName -in $TypeAcceleratorsClass::Get.Keys) {
    $Message = @(
      "Unable to register type accelerator '$($Type.FullName)'"
      'Accelerator already exists.'
    ) -join ' - '
    "TypeAcceleratorAlreadyExists $Message" | Write-Debug
  }
}
# Add type accelerators for every exportable type.
foreach ($Type in $typestoExport) {
  $TypeAcceleratorsClass::Add($Type.FullName, $Type)
}
# Remove type accelerators when the module is removed.
$MyInvocation.MyCommand.ScriptBlock.Module.OnRemove = {
  foreach ($Type in $typestoExport) {
    $TypeAcceleratorsClass::Remove($Type.FullName)
  }
}.GetNewClosure();

$scripts = @();
$Public = Get-ChildItem "$PSScriptRoot/Public" -Filter "*.ps1" -Recurse -ErrorAction SilentlyContinue
$scripts += Get-ChildItem "$PSScriptRoot/Private" -Filter "*.ps1" -Recurse -ErrorAction SilentlyContinue
$scripts += $Public

foreach ($file in $scripts) {
  Try {
    if ([string]::IsNullOrWhiteSpace($file.fullname)) { continue }
    . "$($file.fullname)"
  } Catch {
    Write-Warning "Failed to import function $($file.BaseName): $_"
    $host.UI.WriteErrorLine($_)
  }
}

$Param = @{
  Function = $Public.BaseName
  Cmdlet   = '*'
  Alias    = '*'
  Verbose  = $false
}
Export-ModuleMember @Param