Skip to content
This repository was archived by the owner on May 29, 2026. It is now read-only.

Commit 4b6c347

Browse files
committed
openai compatibility
Signed-off-by: va <va@us.ibm.com>
1 parent 3740a5f commit 4b6c347

4 files changed

Lines changed: 88 additions & 61 deletions

File tree

src/services/codeAssistant.ts

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,9 @@ import { SSEParser, StreamingPipeline } from "../utilities/streamingPipeline";
2121
import { streamingStatusBar, StreamingTelemetry } from "../utilities/streamingStatusBar";
2222
import { isRetryableError } from "../utilities/errorUtils";
2323
import { getCircuitBreaker } from "../utilities/circuitBreaker";
24+
import { modelTransform, toModelPromptResponse } from "../utilities/utils";
2425

26+
const QCA_API_VERSION = "v1";
2527
const SERVICE_NAME = "qiskit-code-assistant";
2628
const STREAM_DATA_PREFIX = 'data: ';
2729

@@ -46,24 +48,25 @@ export default class CodeAssistantService extends ServiceAPI {
4648
}
4749

4850
async getModels(): Promise<ModelInfo[]> {
49-
// GET /models
50-
const endpoint = "/models";
51+
// GET /v1/models
52+
const endpoint = `/${QCA_API_VERSION}/models`;
5153
const apiToken = await this.getApiToken()
5254
const options = {
5355
"method": "GET",
5456
"headers": ServiceAPI.getHeaders(apiToken)
5557
};
5658

5759
const response = await ServiceAPI.runFetch(endpoint, options);
58-
const jsonResponse = (await response.json()) as ModelsList;
59-
const modelsData = jsonResponse["models"];
60-
61-
return modelsData;
60+
const jsonResponse = (await response.json()) as OpenAIModelList;
61+
const modelsData = jsonResponse["data"];
62+
const updatedModelsData = modelsData.map(modelTransform);
63+
64+
return updatedModelsData;
6265
}
6366

6467
async getModel(modelId: string): Promise<ModelInfo> {
65-
// GET /model/{modelId}
66-
const endpoint = `/model/${modelId}`;
68+
// GET /v1/models/{model_id}
69+
const endpoint = `/${QCA_API_VERSION}/models/${modelId}`;
6770
const apiToken = await this.getApiToken()
6871
const options = {
6972
"method": "GET",
@@ -77,8 +80,8 @@ export default class CodeAssistantService extends ServiceAPI {
7780
}
7881

7982
async getModelDisclaimer(modelId: string): Promise<ModelDisclaimer> {
80-
// GET /model/{modelId}/disclaimer
81-
const endpoint = `/model/${modelId}/disclaimer`;
83+
// GET /v1/models/{modelId}/disclaimer
84+
const endpoint = `/${QCA_API_VERSION}/models/${modelId}/disclaimer`;
8285
const apiToken = await this.getApiToken()
8386
const options = {
8487
"method": "GET",
@@ -96,14 +99,14 @@ export default class CodeAssistantService extends ServiceAPI {
9699
disclaimerId: string,
97100
accepted: boolean
98101
): Promise<ResponseMessage> {
99-
// POST /disclaimer/{disclaimerId}/acceptance
100-
const endpoint = `/disclaimer/${disclaimerId}/acceptance`;
102+
// POST /v1/models/{modelId}/disclaimer
103+
const endpoint = `/${QCA_API_VERSION}/models/${modelId}/disclaimer`;
101104
const apiToken = await this.getApiToken()
102105
const options = {
103106
"method": "POST",
104107
"headers": ServiceAPI.getHeaders(apiToken),
105108
"body": JSON.stringify({
106-
"model": modelId,
109+
"disclaimer": disclaimerId,
107110
accepted
108111
})
109112
};
@@ -120,8 +123,8 @@ export default class CodeAssistantService extends ServiceAPI {
120123
signal?: AbortSignal,
121124
retryCount: number = 0
122125
): AsyncGenerator<ModelPromptResponse> {
123-
// POST /model/{modelId}/prompt
124-
const endpoint = `/model/${modelId}/prompt`;
126+
// POST /v1/completions
127+
const endpoint = `/${QCA_API_VERSION}/chat/completions`;
125128
const apiToken = await this.getApiToken()
126129

127130
// Fetch config at runtime for hot-reload support
@@ -140,7 +143,11 @@ export default class CodeAssistantService extends ServiceAPI {
140143
"method": "POST",
141144
"headers": ServiceAPI.getHeaders(apiToken),
142145
"body": JSON.stringify({
143-
input,
146+
model: modelId,
147+
messages: [{
148+
role: "user",
149+
content: input
150+
}],
144151
stream: streamingEnabled
145152
})
146153
};
@@ -203,11 +210,20 @@ export default class CodeAssistantService extends ServiceAPI {
203210
const rawStream = ServiceAPI.runFetchStreaming(endpoint, options);
204211

205212
// Parse SSE format with unified parser
206-
const parser = new SSEParser<ModelPromptResponse>(STREAM_DATA_PREFIX);
213+
const parser = new SSEParser<OpenAIPromptResponse>(STREAM_DATA_PREFIX);
207214
const parsedStream = parser.parse(rawStream);
208215

216+
// Transform OpenAI format to ModelPromptResponse
217+
async function* transformStream(stream: AsyncGenerator<OpenAIPromptResponse>): AsyncGenerator<ModelPromptResponse> {
218+
for await (const openAIChunk of stream) {
219+
yield toModelPromptResponse(openAIChunk);
220+
}
221+
}
222+
223+
const transformedStream = transformStream(parsedStream);
224+
209225
// Wrap in streaming pipeline for metrics and monitoring
210-
const pipeline = new StreamingPipeline(parsedStream, {
226+
const pipeline = new StreamingPipeline(transformedStream, {
211227
signal: effectiveSignal,
212228
enableMetrics,
213229
bufferSize,
@@ -304,7 +320,8 @@ export default class CodeAssistantService extends ServiceAPI {
304320
}
305321
} else {
306322
const response = await ServiceAPI.runFetch(endpoint, options)
307-
const promptResponse = (await response.json()) as ModelPromptResponse;
323+
const jsonResponse = (await response.json()) as OpenAIPromptResponse;
324+
const promptResponse = toModelPromptResponse(jsonResponse)
308325
yield promptResponse;
309326
}
310327
}
@@ -325,13 +342,14 @@ export default class CodeAssistantService extends ServiceAPI {
325342
return { success: false }
326343
}
327344

328-
// POST /prompt/{promptId}/acceptance
329-
const endpoint = `/prompt/${promptId}/acceptance`;
345+
// POST /v1/completion/acceptance
346+
const endpoint = `/${QCA_API_VERSION}/completion/acceptance`;
330347
const apiToken = await this.getApiToken()
331348
const options = {
332349
"method": "POST",
333350
"headers": ServiceAPI.getHeaders(apiToken),
334351
"body": JSON.stringify({
352+
completion: promptId,
335353
accepted
336354
})
337355
};

src/services/openAI.ts

Lines changed: 1 addition & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -19,49 +19,11 @@ import { SSEParser, StreamingPipeline } from "../utilities/streamingPipeline";
1919
import { streamingStatusBar, StreamingTelemetry } from "../utilities/streamingStatusBar";
2020
import { isRetryableError } from "../utilities/errorUtils";
2121
import { getCircuitBreaker } from "../utilities/circuitBreaker";
22-
23-
type OpenAIModelInfo = {
24-
id: string
25-
}
26-
type OpenAIModelList = {
27-
data: OpenAIModelInfo[]
28-
}
29-
type OpenAIChoice = {
30-
index: number,
31-
text: string
32-
}
33-
type OpenAIPromptResponse = {
34-
id: string,
35-
created: number,
36-
choices: OpenAIChoice[]
37-
}
22+
import { modelTransform, toModelPromptResponse } from "../utilities/utils";
3823

3924
const OPENAI_API_VERSION = "v1";
4025
const SERVICE_NAME = "open-ai";
4126

42-
function modelTransform (model: OpenAIModelInfo): ModelInfo {
43-
return {
44-
"_id": model.id,
45-
"disclaimer": { accepted: true },
46-
"display_name": model.id,
47-
"doc_link": "",
48-
"license": { name: "", link: "" },
49-
"model_id": model.id
50-
}
51-
}
52-
53-
function toModelPromptResponse(jsonResponse: OpenAIPromptResponse): ModelPromptResponse {
54-
const responseText = jsonResponse["choices"].map(c => {
55-
return { "generated_text": c.text };
56-
});
57-
const promptResponse: ModelPromptResponse = {
58-
results: responseText,
59-
prompt_id: jsonResponse["id"],
60-
created_at: (new Date(jsonResponse["created"])).toISOString()
61-
}
62-
return promptResponse
63-
}
64-
6527
export default class OpenAIService extends ServiceAPI {
6628
get name() { return SERVICE_NAME; }
6729

src/utilities/utils.ts

Lines changed: 23 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -75,7 +75,6 @@ export function normalizeURLPath(urlPath: string): string {
7575
return urlPath;
7676
}
7777

78-
7978
export function createDecorationType(): vscode.TextEditorDecorationType {
8079
return vscode.window.createTextEditorDecorationType({
8180
after: {
@@ -128,3 +127,26 @@ export function toCompletionItem(
128127
resultEntry.completion_metadata?.snippet_context
129128
)
130129
}
130+
131+
export function toModelPromptResponse(jsonResponse: OpenAIPromptResponse): ModelPromptResponse {
132+
const responseText = jsonResponse["choices"].map(c => {
133+
return { "generated_text": c.text ?? c.message?.content };
134+
});
135+
const promptResponse: ModelPromptResponse = {
136+
results: responseText,
137+
prompt_id: jsonResponse["id"],
138+
created_at: (new Date(jsonResponse["created"])).toISOString()
139+
}
140+
return promptResponse
141+
}
142+
143+
export function modelTransform (model: OpenAIModelInfo): ModelInfo {
144+
return {
145+
"_id": model.id,
146+
"disclaimer": { accepted: true },
147+
"display_name": model.id,
148+
"doc_link": "",
149+
"license": { name: "", link: "" },
150+
"model_id": model.id
151+
}
152+
}

types/index.d.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -122,3 +122,28 @@ interface QiskitAccountJson {
122122
token?: string
123123
}
124124
}
125+
126+
interface OpenAIModelInfo {
127+
id: string
128+
}
129+
130+
interface OpenAIModelList {
131+
data: OpenAIModelInfo[]
132+
}
133+
134+
interface OpenAIMessage {
135+
role: string
136+
content: string
137+
}
138+
139+
interface OpenAIChoice {
140+
index: number,
141+
text?: string
142+
message?: OpenAIMessage
143+
}
144+
145+
interface OpenAIPromptResponse {
146+
id: string,
147+
created: number,
148+
choices: OpenAIChoice[]
149+
}

0 commit comments

Comments
 (0)