24 public class AIProvider
26 private readonly AIProviderType _providerType;
34 _providerType = providerType;
35 _restClient = restClient ??
throw new ArgumentNullException(nameof(restClient));
36 _credentials = credentials;
40 if (_definition ==
null)
41 GPAL.
PublishSimpleEvent(GPALEventType.WARNING, $
"No provider definition found for [{providerType}]. Add it to AIProvidersConfig.yaml.",
null, GPALObjectType.AIProvider);
44 public string Call(
string prompt)
48 if (
string.IsNullOrEmpty(prompt))
54 string endpoint = GetEndpoint();
55 string apiBase = GetApiBase();
57 string accessToken =
null;
58 if (_credentials !=
null)
60 _credentials.FetchAccessToken(out accessToken);
61 if (
string.IsNullOrEmpty(accessToken))
62 GPAL.
PublishSimpleEvent(GPALEventType.ERROR,
"Unable to login using credentials. No access token granted.",
this, GPALObjectType.AIProvider);
65 var body = BuildRequestBody(prompt);
70 .WithAPIBase(apiBase, System.Threading.Timeout.InfiniteTimeSpan)
71 .WithEndpoint(endpoint);
73 if (!
string.IsNullOrEmpty(accessToken))
75 var headerName = _definition?.AuthHeader ??
"Authorization";
76 var prefix = _definition?.AuthPrefix ??
"Bearer ";
77 _restClient.WithHeader(headerName,
string.IsNullOrEmpty(prefix) ? accessToken : $
"{prefix}{accessToken}");
80 if (_definition?.AdditionalHeaders !=
null)
82 foreach (var h
in _definition.AdditionalHeaders)
83 _restClient.WithHeader(h.Key, h.Value);
86 string response = requestBuilder
90 if (
string.IsNullOrEmpty(response))
92 GPAL.
PublishSimpleEvent(GPALEventType.ERROR,
"Empty response from AI provider.",
this, GPALObjectType.AIProvider);
96 string completion = ExtractCompletion(response);
97 if (
string.IsNullOrEmpty(completion))
99 GPAL.
PublishSimpleEvent(GPALEventType.ERROR, $
"Unable to extract completion from [{_providerType}] response: [{response}]",
this, GPALObjectType.AIProvider);
103 GPAL.
PublishSimpleEvent(GPALEventType.INFO, $
"Received response from [{_providerType}]",
this, GPALObjectType.AIProvider);
108 GPAL.
PublishSimpleEvent(GPALEventType.EXCEPTION, $
"Failed to call AI provider",
this, GPALObjectType.AIProvider, ex);
113 private Dictionary<string, object> BuildRequestBody(
string prompt)
115 var messages =
new List<object> {
new Dictionary<string, object> { [
"role"] =
"user", [
"content"] = prompt } };
117 var body =
new Dictionary<string, object>
119 [
"model"] = GetModel(),
120 [
"messages"] = messages
123 if (_definition?.ResponseFormat ==
"anthropic")
125 body[
"max_tokens"] = GetMaxTokens();
128 if (_config.AdditionalParameters !=
null)
130 foreach (var param
in _config.AdditionalParameters)
132 if (IsReservedParameter(param.Key))
134 body[param.Key] = param.Value;
141 private static bool IsReservedParameter(
string key)
143 return key ==
"Endpoint" || key ==
"Model" || key ==
"MaxTokens" || key ==
"AnthropicVersion";
146 private int GetMaxTokens()
148 if (_config?.AdditionalParameters?.ContainsKey(
"MaxTokens") ==
true
149 &&
int.TryParse(_config.AdditionalParameters[
"MaxTokens"].ToString(), out
int maxTokens))
156 private string GetModel()
158 if (_config?.AdditionalParameters?.ContainsKey(
"Model") ==
true)
159 return _config.AdditionalParameters[
"Model"].ToString();
160 return _definition?.DefaultModel;
163 private string GetApiBase() => _definition?.BaseUrl;
165 private string GetEndpoint()
167 if (_config?.AdditionalParameters?.ContainsKey(
"Endpoint") ==
true)
168 return _config.AdditionalParameters[
"Endpoint"].ToString();
169 return _definition?.ChatEndpoint ??
"/chat/completions";
172 private string ExtractCompletion(
string response)
174 using var doc = JsonDocument.Parse(response);
175 var root = doc.RootElement;
177 if (_definition?.ResponseFormat ==
"anthropic")
179 if (root.TryGetProperty(
"content", out var content) && content.ValueKind == JsonValueKind.Array && content.GetArrayLength() > 0)
181 var first = content[0];
182 if (first.TryGetProperty(
"text", out var text))
183 return text.GetString();
188 if (root.TryGetProperty(
"choices", out var choices) && choices.ValueKind == JsonValueKind.Array && choices.GetArrayLength() > 0)
190 var first = choices[0];
191 if (first.TryGetProperty(
"message", out var message) && message.TryGetProperty(
"content", out var msgContent))
192 return msgContent.GetString();