@gitkraken/ai-provider-tools 0.1.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +9 -3
- package/dist/config.d.ts +6 -0
- package/dist/providers/gitkraken.js +1 -1
- package/package.json +4 -4
package/CHANGELOG.md
CHANGED
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
## 0.3.0
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
### Minor Changes
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
- [#247](https://github.com/gitkraken/core-tools/pull/247) [`998e498`](https://github.com/gitkraken/core-tools/commit/998e4981ec70457e60444f180668ccbd578022ee) Thanks [@JBUinfo](https://github.com/JBUinfo)! - The hosted task `ai.generate` telemetry event now carries `action` (the task action) and `duration_ms` (the task HTTP call duration); chat and BYOK events are unchanged.
|
|
8
|
+
|
|
9
|
+
## [0.2.0] - 2026-10-26
|
|
10
|
+
|
|
11
|
+
### Added
|
|
12
|
+
|
|
13
|
+
- `AiProviderConfig.conversationId`: on the `gitkraken` provider, `createModel(config).generate` sends it as `GK-Conversation-ID` on every chat/completions call, as gkcli does. The task route (generators) sends no conversation header, an empty string sends none, and BYOK providers ignore the field. The package never generates an id, so existing callers are unchanged.
|
|
8
14
|
|
|
9
15
|
## [0.1.1] - 2026-10-02
|
|
10
16
|
|
package/dist/config.d.ts
CHANGED
|
@@ -33,6 +33,12 @@ interface AiProviderConfig {
|
|
|
33
33
|
* route rejects requests without it (HTTP 403).
|
|
34
34
|
*/
|
|
35
35
|
organizationId?: string;
|
|
36
|
+
/**
|
|
37
|
+
* `gitkraken` only: sent as `GK-Conversation-ID` on every `createModel(config).generate` (chat/completions)
|
|
38
|
+
* call; not on the task route (generators), matching gkcli. An empty string sends no header. Ignored by
|
|
39
|
+
* BYOK providers. Never auto-generated: the host supplies it.
|
|
40
|
+
*/
|
|
41
|
+
conversationId?: string;
|
|
36
42
|
/** `gitkraken`: sent as the `Client-Name`/`Client-Version` headers. `openrouter`: `name` also becomes OpenRouter's `X-Title` (default `GitKraken`). */
|
|
37
43
|
client?: {
|
|
38
44
|
name: string;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
import{AiError as e,isAbortError as t,toAiError as n,wireErrorFields as r}from"../errors.js";import{classifyGitKrakenHttpError as i}from"./gitkraken-errors.js";import{toWireMessages as a,toWireTools as o,wireContent as s}from"./gitkraken-wire.js";import{normalizeFinishReason as c,parseOpenAICompatibleStream as l}from"./gitkraken-stream.js";import{defaultRequest as u}from"@gitkraken/api-tools";const d=`message-prompt`;function f(e,t=``){let n=e.baseUrl??`https://api.gitkraken.dev/`,r=n.endsWith(`/`)?n:`${n}/`;return new URL(`v1/ai-tasks${t}`,r).toString()}async function p(n){if(!n.getToken)throw new e(`not_logged_in`,"the gitkraken provider requires `getToken` to be configured");try{return await n.getToken()}catch(n){throw t(n)?n:new e(`not_logged_in`,`failed to obtain a GitKraken token`,{cause:n})}}async function m(e,t){let n={Authorization:`Bearer ${await p(e)}`};return e.organizationId&&(n[`GK-Org-Id`]=e.organizationId),t&&(n[`GK-Action`]=t),e.client&&(n[`Client-Name`]=e.client.name,n[`Client-Version`]=e.client.version),n}function h(e){return typeof e==`string`?e:e==null?``:JSON.stringify(e)}function g(t){if(typeof t==`string`){if(t.length===0)return{};try{return JSON.parse(t)}catch(t){throw new e(`provider_error`,`AI service returned a malformed response`,{cause:t})}}return t??{}}function _(e,t){!t||!e.onCreditUsage||t.limit==null||t.limit<=0||t.used==null||e.onCreditUsage({used:t.used,limit:t.limit,resetsOn:t.resetsOn&&!Number.isNaN(Date.parse(t.resetsOn))?new Date(t.resetsOn).toISOString():t.resetsOn,organizationId:e.organizationId})}async function v(e,t){try{let n=a(t.messages);t.system&&n.unshift({role:`system`,content:s(t.system,!1)});let r={model:t.model||void 0,messages:n,max_tokens:t.maxTokens||void 0,stream:!0,stream_options:{include_usage:!0},temperature:t.temperature??0,tools:o(t.tools)},u=await m(e,t.action||`compose-changes`);u[`Content-Type`]=`application/json`,u.Accept=`text/event-stream`,t.conversationId&&(u[`GK-Conversation-ID`]=t.conversationId);let d=await(e.fetch??globalThis.fetch)(f(e,`/chat/completions`),{method:`POST`,redirect:`manual`,headers:u,body:JSON.stringify(r),signal:t.signal}),p=await d.text();if(!d.ok)throw await i(d.status,p,e);let h=l(p);return _(e,h.gk),e.telemetry?.event(`ai.generate`,{provider:`gitkraken`,kind:`chat`}),{text:h.content,toolCalls:h.toolCalls.length>0?h.toolCalls:void 0,usage:h.promptTokens||h.completionTokens||h.totalTokens?{inputTokens:h.promptTokens,outputTokens:h.completionTokens}:void 0,finishReason:c(h.finishReason),rawFinishReason:h.finishReason,...h.model?{model:h.model}:{}}}catch(t){throw e.telemetry?.exception(t,{provider:`gitkraken`,kind:`chat`}),n(t,`AI request failed`)}}function y(e){return{async generate(t){let n=t.messages,r=`tools`in t?t.tools:void 0;return v(e,{model:e.model,system:t.system,messages:n,tools:r,maxTokens:t.maxTokens,temperature:t.temperature,signal:t.signal})}}}async function b(t,a){try{let n=t.request??u,o={taskType:d,action:a.action,messages:[{content:a.prompt}],model:(a.model??t.model)||void 0},s=
|
|
1
|
+
import{AiError as e,isAbortError as t,toAiError as n,wireErrorFields as r}from"../errors.js";import{classifyGitKrakenHttpError as i}from"./gitkraken-errors.js";import{toWireMessages as a,toWireTools as o,wireContent as s}from"./gitkraken-wire.js";import{normalizeFinishReason as c,parseOpenAICompatibleStream as l}from"./gitkraken-stream.js";import{defaultRequest as u}from"@gitkraken/api-tools";const d=`message-prompt`;function f(e,t=``){let n=e.baseUrl??`https://api.gitkraken.dev/`,r=n.endsWith(`/`)?n:`${n}/`;return new URL(`v1/ai-tasks${t}`,r).toString()}async function p(n){if(!n.getToken)throw new e(`not_logged_in`,"the gitkraken provider requires `getToken` to be configured");try{return await n.getToken()}catch(n){throw t(n)?n:new e(`not_logged_in`,`failed to obtain a GitKraken token`,{cause:n})}}async function m(e,t){let n={Authorization:`Bearer ${await p(e)}`};return e.organizationId&&(n[`GK-Org-Id`]=e.organizationId),t&&(n[`GK-Action`]=t),e.client&&(n[`Client-Name`]=e.client.name,n[`Client-Version`]=e.client.version),n}function h(e){return typeof e==`string`?e:e==null?``:JSON.stringify(e)}function g(t){if(typeof t==`string`){if(t.length===0)return{};try{return JSON.parse(t)}catch(t){throw new e(`provider_error`,`AI service returned a malformed response`,{cause:t})}}return t??{}}function _(e,t){!t||!e.onCreditUsage||t.limit==null||t.limit<=0||t.used==null||e.onCreditUsage({used:t.used,limit:t.limit,resetsOn:t.resetsOn&&!Number.isNaN(Date.parse(t.resetsOn))?new Date(t.resetsOn).toISOString():t.resetsOn,organizationId:e.organizationId})}async function v(e,t){try{let n=a(t.messages);t.system&&n.unshift({role:`system`,content:s(t.system,!1)});let r={model:t.model||void 0,messages:n,max_tokens:t.maxTokens||void 0,stream:!0,stream_options:{include_usage:!0},temperature:t.temperature??0,tools:o(t.tools)},u=await m(e,t.action||`compose-changes`);u[`Content-Type`]=`application/json`,u.Accept=`text/event-stream`,t.conversationId&&(u[`GK-Conversation-ID`]=t.conversationId);let d=await(e.fetch??globalThis.fetch)(f(e,`/chat/completions`),{method:`POST`,redirect:`manual`,headers:u,body:JSON.stringify(r),signal:t.signal}),p=await d.text();if(!d.ok)throw await i(d.status,p,e);let h=l(p);return _(e,h.gk),e.telemetry?.event(`ai.generate`,{provider:`gitkraken`,kind:`chat`}),{text:h.content,toolCalls:h.toolCalls.length>0?h.toolCalls:void 0,usage:h.promptTokens||h.completionTokens||h.totalTokens?{inputTokens:h.promptTokens,outputTokens:h.completionTokens}:void 0,finishReason:c(h.finishReason),rawFinishReason:h.finishReason,...h.model?{model:h.model}:{}}}catch(t){throw e.telemetry?.exception(t,{provider:`gitkraken`,kind:`chat`}),n(t,`AI request failed`)}}function y(e){return{async generate(t){let n=t.messages,r=`tools`in t?t.tools:void 0;return v(e,{model:e.model,conversationId:e.conversationId,system:t.system,messages:n,tools:r,maxTokens:t.maxTokens,temperature:t.temperature,signal:t.signal})}}}async function b(t,a){try{let n=t.request??u,o={taskType:d,action:a.action,messages:[{content:a.prompt}],model:(a.model??t.model)||void 0},s={...await m(t,a.action),"Content-Type":`application/json`},c=performance.now(),l=await n({method:`POST`,url:f(t),headers:s,body:o,signal:a.signal}),p=Math.max(0,Math.round(performance.now()-c));if(!(l.status>=200&&l.status<300))throw await i(l.status,h(l.body),t);let v=g(l.body);if(!S(v))throw C(`task`);let y=v.error;if(y!=null){let t=r(v);throw new e(`provider_error`,`AI request failed: ${S(y)&&typeof y.message==`string`?y.message:`unknown provider error`}`,t)}let b=v.data;if(!S(b))throw C(`task`);if(b.status===`error`||b.status===`failed`)throw new e(`provider_error`,`AI task failed: ${String(b.status)}`,{...r(b),...typeof b.code==`string`?{providerErrorCode:b.code}:{},...typeof b.type==`string`?{providerErrorType:b.type}:{}});if(!S(b.result)||typeof b.result.content!=`string`)throw C(`task`);let x=b.result.content,w=b,T=w.usage;return _(t,T?.gk),t.telemetry?.event(`ai.generate`,{provider:`gitkraken`,kind:`task`,action:a.action,duration_ms:p}),{text:x,...T?.modelId?{model:T.modelId}:{},...w.id?{task:{id:w.id,...w.status?{status:w.status}:{},...w.provider?{provider:w.provider}:{}}}:{},...T?.promptTokens||T?.completionTokens?{usage:{inputTokens:T.promptTokens??0,outputTokens:T.completionTokens??0}}:{}}}catch(e){throw t.telemetry?.exception(e,{provider:`gitkraken`,kind:`task`}),n(e,`AI request failed`)}}async function x(t,a){let o=t.request??u,s;try{let e=await o({method:`GET`,url:f(t,`/providers/${a||d}`),headers:await m(t,void 0)});if(!(e.status>=200&&e.status<300))throw await i(e.status,h(e.body),t);s=g(e.body)}catch(e){throw n(e,`failed to list AI models`)}if(!S(s))throw C(`model list`);if(s.error!=null)throw new e(`provider_error`,`AI model list request returned an error response`,r(s));if(!Array.isArray(s.data))throw C(`model list`);return s.data.map(e=>{if(!S(e)||typeof e.providerId!=`string`||!e.providerId||typeof e.modelId!=`string`||!e.modelId||e.providerName!==void 0&&typeof e.providerName!=`string`||e.modelName!==void 0&&typeof e.modelName!=`string`||e.consumptionRateLabel!==void 0&&typeof e.consumptionRateLabel!=`string`||e.maxInputTokens!==void 0&&typeof e.maxInputTokens!=`number`||e.maxOutputTokens!==void 0&&typeof e.maxOutputTokens!=`number`||e.preferred!==void 0&&typeof e.preferred!=`boolean`)throw C(`model list`);let t=e;return{providerId:t.providerId,providerName:t.providerName,modelId:t.modelId,modelName:t.modelName,maxInputTokens:t.maxInputTokens,maxOutputTokens:t.maxOutputTokens,preferred:t.preferred??!1,consumptionRateLabel:t.consumptionRateLabel}})}function S(e){return typeof e==`object`&&!!e&&!Array.isArray(e)}function C(t){return new e(`provider_error`,`AI service returned an invalid ${t} response`)}export{v as chatComplete,y as createGitKrakenModel,x as listModels,b as taskComplete};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gitkraken/ai-provider-tools",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "AI text generation for GitKraken client SDKs: GitKraken-hosted and BYOK (Anthropic, OpenAI, OpenRouter, Ollama) providers behind one model port.",
|
|
5
5
|
"license": "SEE LICENSE IN LICENSE",
|
|
6
6
|
"author": "Axosoft, LLC dba GitKraken",
|
|
@@ -29,14 +29,14 @@
|
|
|
29
29
|
"@openrouter/ai-sdk-provider": "^2.3.3",
|
|
30
30
|
"ai": "^6.0.138",
|
|
31
31
|
"ollama-ai-provider-v2": "^3.5.0",
|
|
32
|
-
"@gitkraken/
|
|
33
|
-
"@gitkraken/
|
|
32
|
+
"@gitkraken/shared-tools": "^0.3.0",
|
|
33
|
+
"@gitkraken/api-tools": "^0.3.0"
|
|
34
34
|
},
|
|
35
35
|
"devDependencies": {
|
|
36
36
|
"@types/node": "^22.0.0",
|
|
37
37
|
"typescript": "^6.0.2",
|
|
38
38
|
"vitest": "^4.1.1",
|
|
39
|
-
"@gitkraken/conflict-tools": "0.
|
|
39
|
+
"@gitkraken/conflict-tools": "0.8.0"
|
|
40
40
|
},
|
|
41
41
|
"scripts": {
|
|
42
42
|
"build": "tsdown --config-loader=unrun",
|