Skip to content

Commit e7685f8

Browse files
author
Jefferson Pires
committed
Refactor to remove the reference to OpenAI_Api package and added his source on the project as project. Continuation of the implementation of SqlServerAgent
1 parent 832a32b commit e7685f8

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

62 files changed

+4912
-446
lines changed

Copilot/InlinePredictionManager.cs

+2-2
Original file line numberDiff line numberDiff line change
@@ -75,8 +75,8 @@ public async Task OnEnterPressed(OptionPageGridGeneral options)
7575
}
7676

7777
string prediction = options.UseCompletion && options.Service == OpenAIService.OpenAI
78-
? await ChatGPT.GetCompletionResponseAsync(options, systemMessage, code, null, cancellationTokenSource.Token)
79-
: await ChatGPT.GetResponseAsync(options, systemMessage, code, null, cancellationTokenSource.Token);
78+
? await ApiHandler.GetCompletionResponseAsync(options, systemMessage, code, null, cancellationTokenSource.Token)
79+
: await ApiHandler.GetResponseAsync(options, systemMessage, code, null, cancellationTokenSource.Token);
8080

8181
if (cancellationTokenSource.Token.IsCancellationRequested || string.IsNullOrWhiteSpace(prediction))
8282
{

OpenAI_API/APIAuthentication.cs

+205
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Diagnostics;
4+
using System.IO;
5+
using System.Threading.Tasks;
6+
7+
namespace OpenAI_API
8+
{
9+
/// <summary>
10+
/// Represents authentication to the OpenAPI API endpoint
11+
/// </summary>
12+
public class APIAuthentication
13+
{
14+
/// <summary>
15+
/// The API key, required to access the API endpoint.
16+
/// </summary>
17+
public string ApiKey { get; set; }
18+
/// <summary>
19+
/// The Organization ID to count API requests against. This can be found at https://beta.openai.com/account/org-settings.
20+
/// </summary>
21+
public string OpenAIOrganization { get; set; }
22+
23+
/// <summary>
24+
/// Allows implicit casting from a string, so that a simple string API key can be provided in place of an instance of <see cref="APIAuthentication"/>
25+
/// </summary>
26+
/// <param name="key">The API key to convert into a <see cref="APIAuthentication"/>.</param>
27+
public static implicit operator APIAuthentication(string key)
28+
{
29+
return new APIAuthentication(key);
30+
}
31+
32+
/// <summary>
33+
/// Instantiates a new Authentication object with the given <paramref name="apiKey"/>, which may be <see langword="null"/>.
34+
/// </summary>
35+
/// <param name="apiKey">The API key, required to access the API endpoint.</param>
36+
public APIAuthentication(string apiKey)
37+
{
38+
this.ApiKey = apiKey;
39+
}
40+
41+
42+
/// <summary>
43+
/// Instantiates a new Authentication object with the given <paramref name="apiKey"/>, which may be <see langword="null"/>. For users who belong to multiple organizations, you can specify which organization is used. Usage from these API requests will count against the specified organization's subscription quota.
44+
/// </summary>
45+
/// <param name="apiKey">The API key, required to access the API endpoint.</param>
46+
/// <param name="openAIOrganization">The Organization ID to count API requests against. This can be found at https://beta.openai.com/account/org-settings.</param>
47+
public APIAuthentication(string apiKey, string openAIOrganization)
48+
{
49+
this.ApiKey = apiKey;
50+
this.OpenAIOrganization = openAIOrganization;
51+
}
52+
53+
private static APIAuthentication cachedDefault = null;
54+
55+
/// <summary>
56+
/// The default authentication to use when no other auth is specified. This can be set manually, or automatically loaded via environment variables or a config file. <seealso cref="LoadFromEnv"/><seealso cref="LoadFromPath(string, string, bool)"/>
57+
/// </summary>
58+
public static APIAuthentication Default
59+
{
60+
get
61+
{
62+
if (cachedDefault != null)
63+
return cachedDefault;
64+
65+
APIAuthentication auth = LoadFromEnv();
66+
if (auth == null)
67+
auth = LoadFromPath();
68+
if (auth == null)
69+
auth = LoadFromPath(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile));
70+
71+
cachedDefault = auth;
72+
return auth;
73+
}
74+
set
75+
{
76+
cachedDefault = value;
77+
}
78+
}
79+
80+
/// <summary>
81+
/// Attempts to load api key from environment variables, as "OPENAI_KEY" or "OPENAI_API_KEY". Also loads org if from "OPENAI_ORGANIZATION" if present.
82+
/// </summary>
83+
/// <returns>Returns the loaded <see cref="APIAuthentication"/> any api keys were found, or <see langword="null"/> if there were no matching environment vars.</returns>
84+
public static APIAuthentication LoadFromEnv()
85+
{
86+
string key = Environment.GetEnvironmentVariable("OPENAI_KEY");
87+
88+
if (string.IsNullOrEmpty(key))
89+
{
90+
key = Environment.GetEnvironmentVariable("OPENAI_API_KEY");
91+
92+
if (string.IsNullOrEmpty(key))
93+
return null;
94+
}
95+
96+
string org = Environment.GetEnvironmentVariable("OPENAI_ORGANIZATION");
97+
98+
return new APIAuthentication(key, org);
99+
}
100+
101+
/// <summary>
102+
/// Attempts to load api keys from a configuration file, by default ".openai" in the current directory, optionally traversing up the directory tree
103+
/// </summary>
104+
/// <param name="directory">The directory to look in, or <see langword="null"/> for the current directory</param>
105+
/// <param name="filename">The filename of the config file</param>
106+
/// <param name="searchUp">Whether to recursively traverse up the directory tree if the <paramref name="filename"/> is not found in the <paramref name="directory"/></param>
107+
/// <returns>Returns the loaded <see cref="APIAuthentication"/> any api keys were found, or <see langword="null"/> if it was not successful in finding a config (or if the config file didn't contain correctly formatted API keys)</returns>
108+
public static APIAuthentication LoadFromPath(string directory = null, string filename = ".openai", bool searchUp = true)
109+
{
110+
if (directory == null)
111+
directory = Environment.CurrentDirectory;
112+
113+
string key = null;
114+
string org = null;
115+
var curDirectory = new DirectoryInfo(directory);
116+
117+
while (key == null && curDirectory.Parent != null)
118+
{
119+
if (File.Exists(Path.Combine(curDirectory.FullName, filename)))
120+
{
121+
var lines = File.ReadAllLines(Path.Combine(curDirectory.FullName, filename));
122+
foreach (var l in lines)
123+
{
124+
var parts = l.Split('=', ':');
125+
if (parts.Length == 2)
126+
{
127+
switch (parts[0].ToUpper())
128+
{
129+
case "OPENAI_KEY":
130+
key = parts[1].Trim();
131+
break;
132+
case "OPENAI_API_KEY":
133+
key = parts[1].Trim();
134+
break;
135+
case "OPENAI_ORGANIZATION":
136+
org = parts[1].Trim();
137+
break;
138+
default:
139+
break;
140+
}
141+
}
142+
}
143+
}
144+
145+
if (searchUp)
146+
{
147+
curDirectory = curDirectory.Parent;
148+
}
149+
else
150+
{
151+
break;
152+
}
153+
}
154+
155+
if (string.IsNullOrEmpty(key))
156+
return null;
157+
158+
return new APIAuthentication(key, org);
159+
}
160+
161+
162+
/// <summary>
163+
/// Tests the api key against the OpenAI API, to ensure it is valid. This hits the models endpoint so should not be charged for usage.
164+
/// </summary>
165+
/// <returns><see langword="true"/> if the api key is valid, or <see langword="false"/> if empty or not accepted by the OpenAI API.</returns>
166+
public async Task<bool> ValidateAPIKey()
167+
{
168+
if (string.IsNullOrEmpty(ApiKey))
169+
return false;
170+
171+
var api = new OpenAIAPI(this);
172+
173+
List<Models.Model> results;
174+
175+
try
176+
{
177+
results = await api.Models.GetModelsAsync();
178+
}
179+
catch (Exception ex)
180+
{
181+
Debug.WriteLine(ex.ToString());
182+
return false;
183+
}
184+
185+
return (results.Count > 0);
186+
}
187+
188+
}
189+
190+
internal static class AuthHelpers
191+
{
192+
/// <summary>
193+
/// A helper method to swap out <see langword="null"/> <see cref="APIAuthentication"/> objects with the <see cref="APIAuthentication.Default"/> authentication, possibly loaded from ENV or a config file.
194+
/// </summary>
195+
/// <param name="auth">The specific authentication to use if not <see langword="null"/></param>
196+
/// <returns>Either the provided <paramref name="auth"/> or the <see cref="APIAuthentication.Default"/></returns>
197+
public static APIAuthentication ThisOrDefault(this APIAuthentication auth)
198+
{
199+
if (auth == null)
200+
auth = APIAuthentication.Default;
201+
202+
return auth;
203+
}
204+
}
205+
}

OpenAI_API/ApiResultBase.cs

+59
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
using Newtonsoft.Json;
2+
using OpenAI_API.Models;
3+
using System;
4+
5+
namespace OpenAI_API
6+
{
7+
/// <summary>
8+
/// Represents a result from calling the OpenAI API, with all the common metadata returned from every endpoint
9+
/// </summary>
10+
abstract public class ApiResultBase
11+
{
12+
13+
/// The time when the result was generated
14+
[JsonIgnore]
15+
public DateTime? Created => CreatedUnixTime.HasValue ? (DateTime?)(DateTimeOffset.FromUnixTimeSeconds(CreatedUnixTime.Value).DateTime) : null;
16+
17+
/// <summary>
18+
/// The time when the result was generated in unix epoch format
19+
/// </summary>
20+
[JsonProperty("created")]
21+
public long? CreatedUnixTime { get; set; }
22+
23+
/// <summary>
24+
/// Which model was used to generate this result.
25+
/// </summary>
26+
[JsonProperty("model")]
27+
public Model Model { get; set; }
28+
29+
/// <summary>
30+
/// Object type, ie: text_completion, file, fine-tune, list, etc
31+
/// </summary>
32+
[JsonProperty("object")]
33+
public string Object { get; set; }
34+
35+
/// <summary>
36+
/// The organization associated with the API request, as reported by the API.
37+
/// </summary>
38+
[JsonIgnore]
39+
public string Organization { get; internal set; }
40+
41+
/// <summary>
42+
/// The server-side processing time as reported by the API. This can be useful for debugging where a delay occurs.
43+
/// </summary>
44+
[JsonIgnore]
45+
public TimeSpan ProcessingTime { get; internal set; }
46+
47+
/// <summary>
48+
/// The request id of this API call, as reported in the response headers. This may be useful for troubleshooting or when contacting OpenAI support in reference to a specific request.
49+
/// </summary>
50+
[JsonIgnore]
51+
public string RequestId { get; internal set; }
52+
53+
/// <summary>
54+
/// The Openai-Version used to generate this response, as reported in the response headers. This may be useful for troubleshooting or when contacting OpenAI support in reference to a specific request.
55+
/// </summary>
56+
[JsonIgnore]
57+
public string OpenaiVersion { get; internal set; }
58+
}
59+
}

OpenAI_API/Chat/ChatContent.cs

+92
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
using Newtonsoft.Json;
2+
using System;
3+
4+
namespace OpenAI_API.Chat
5+
{
6+
/// <summary>
7+
/// Represents the base class for chat content, providing a common type identifier.
8+
/// </summary>
9+
public abstract class ChatContentBase
10+
{
11+
/// <summary>
12+
/// Represents the type of the object, initialized with a default value.
13+
/// </summary>
14+
[JsonProperty("type")]
15+
public string Type { get; private set; }
16+
17+
/// <summary>
18+
/// Initializes a new instance of the ChatContentBase class with the specified type.
19+
/// </summary>
20+
/// <param name="type">The type of the chat content.</param>
21+
public ChatContentBase(string type)
22+
{
23+
Type = type;
24+
}
25+
}
26+
27+
/// <summary>
28+
/// Represents the content of a chat message in text format.
29+
/// Inherits from the ChatContentBase class, specifying the content type as "text".
30+
/// </summary>
31+
public class ChatContentForText : ChatContentBase
32+
{
33+
/// <summary>
34+
/// Represents a text property that is serialized to JSON with the key "text".
35+
/// </summary>
36+
[JsonProperty("text")]
37+
public string Text { get; set; }
38+
39+
/// <summary>
40+
/// Initializes a new instance of the ChatContentForText class with the specified text.
41+
/// </summary>
42+
/// <param name="text">The text content to initialize the instance with.</param>
43+
public ChatContentForText(string text) : base(text)
44+
{
45+
Text = text;
46+
}
47+
}
48+
49+
/// <summary>
50+
/// Represents the content of a chat message that contains an image,
51+
/// initialized with the specified image data.
52+
/// </summary>
53+
public class ChatContentForImage : ChatContentBase
54+
{
55+
/// <summary>
56+
/// Represents the URL of an image, initialized with a base64 encoded JPEG image data.
57+
/// </summary>
58+
[JsonProperty("image_url")]
59+
public ImageUrl ImageUrl { get; private set; }
60+
61+
/// <summary>
62+
/// Initializes a new instance of the ChatContentForImage class with the provided image data.
63+
/// Converts the byte array to a Base64 string and constructs an image URL in the "data:image/jpeg;base64" format.
64+
/// </summary>
65+
/// <param name="imageData">The byte array representing the image data.</param>
66+
public ChatContentForImage(byte[] imageData) : base("image_url")
67+
{
68+
ImageUrl = new ImageUrl("data:image/jpeg;base64," + Convert.ToBase64String(imageData));
69+
}
70+
}
71+
72+
/// <summary>
73+
/// Represents an image URL with a specified URL string.
74+
/// </summary>
75+
public class ImageUrl
76+
{
77+
/// <summary>
78+
/// Represents a URL property that can be serialized to and from JSON.
79+
/// </summary>
80+
[JsonProperty("url")]
81+
public string Url { get; set; }
82+
83+
/// <summary>
84+
/// Initializes a new instance of the ImageUrl class with the specified URL.
85+
/// </summary>
86+
/// <param name="url">The URL of the image.</param>
87+
public ImageUrl(string url)
88+
{
89+
Url = url;
90+
}
91+
}
92+
}

0 commit comments

Comments
 (0)