kodu.ts 3.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. import { Anthropic } from "@anthropic-ai/sdk"
  2. import { ApiHandler, withoutImageData } from "."
  3. import { ApiHandlerOptions, koduDefaultModelId, KoduModelId, koduModels, ModelInfo } from "../shared/api"
  4. import axios from "axios"
  5. import * as vscode from "vscode"
  6. const KODU_BASE_URL = "https://claude-dev.com"
  7. export function didClickKoduSignIn() {
  8. const loginUrl = `${KODU_BASE_URL}/auth/login?redirectTo=${vscode.env.uriScheme}://saoudrizwan.claude-dev&ext=1`
  9. vscode.env.openExternal(vscode.Uri.parse(loginUrl))
  10. }
  11. export function didClickKoduAddCredits() {
  12. const addCreditsUrl = `${KODU_BASE_URL}/user/addCredits?redirectTo=${vscode.env.uriScheme}://saoudrizwan.claude-dev&ext=1`
  13. vscode.env.openExternal(vscode.Uri.parse(addCreditsUrl))
  14. }
  15. export async function fetchKoduCredits({ apiKey }: { apiKey: string }) {
  16. const response = await axios.get(`${KODU_BASE_URL}/api/credits`, {
  17. headers: {
  18. "x-api-key": apiKey,
  19. },
  20. })
  21. return (response.data.credits as number) || 0
  22. }
  23. export class KoduHandler implements ApiHandler {
  24. private options: ApiHandlerOptions
  25. constructor(options: ApiHandlerOptions) {
  26. this.options = options
  27. }
  28. async createMessage(
  29. systemPrompt: string,
  30. messages: Anthropic.Messages.MessageParam[],
  31. tools: Anthropic.Messages.Tool[]
  32. ): Promise<Anthropic.Messages.Message> {
  33. const modelId = this.getModel().id
  34. let requestBody: Anthropic.Beta.PromptCaching.Messages.MessageCreateParamsNonStreaming
  35. switch (modelId) {
  36. case "claude-3-5-sonnet-20240620":
  37. case "claude-3-haiku-20240307":
  38. const userMsgIndices = messages.reduce(
  39. (acc, msg, index) => (msg.role === "user" ? [...acc, index] : acc),
  40. [] as number[]
  41. )
  42. const lastUserMsgIndex = userMsgIndices[userMsgIndices.length - 1] ?? -1
  43. const secondLastMsgUserIndex = userMsgIndices[userMsgIndices.length - 2] ?? -1
  44. requestBody = {
  45. model: modelId,
  46. max_tokens: this.getModel().info.maxTokens,
  47. system: [{ text: systemPrompt, type: "text", cache_control: { type: "ephemeral" } }],
  48. messages: messages.map((message, index) => {
  49. if (index === lastUserMsgIndex || index === secondLastMsgUserIndex) {
  50. return {
  51. ...message,
  52. content:
  53. typeof message.content === "string"
  54. ? [
  55. {
  56. type: "text",
  57. text: message.content,
  58. cache_control: { type: "ephemeral" },
  59. },
  60. ]
  61. : message.content.map((content, contentIndex) =>
  62. contentIndex === message.content.length - 1
  63. ? { ...content, cache_control: { type: "ephemeral" } }
  64. : content
  65. ),
  66. }
  67. }
  68. return message
  69. }),
  70. tools,
  71. tool_choice: { type: "auto" },
  72. }
  73. break
  74. default:
  75. requestBody = {
  76. model: modelId,
  77. max_tokens: this.getModel().info.maxTokens,
  78. system: [{ text: systemPrompt, type: "text" }],
  79. messages,
  80. tools,
  81. tool_choice: { type: "auto" },
  82. }
  83. }
  84. const response = await axios.post(`${KODU_BASE_URL}/api/inference`, requestBody, {
  85. headers: {
  86. "x-api-key": this.options.koduApiKey,
  87. },
  88. })
  89. return response.data
  90. }
  91. createUserReadableRequest(
  92. userContent: Array<
  93. | Anthropic.TextBlockParam
  94. | Anthropic.ImageBlockParam
  95. | Anthropic.ToolUseBlockParam
  96. | Anthropic.ToolResultBlockParam
  97. >
  98. ): any {
  99. return {
  100. model: this.getModel().id,
  101. max_tokens: this.getModel().info.maxTokens,
  102. system: "(see SYSTEM_PROMPT in src/ClaudeDev.ts)",
  103. messages: [{ conversation_history: "..." }, { role: "user", content: withoutImageData(userContent) }],
  104. tools: "(see tools in src/ClaudeDev.ts)",
  105. tool_choice: { type: "auto" },
  106. }
  107. }
  108. getModel(): { id: KoduModelId; info: ModelInfo } {
  109. const modelId = this.options.apiModelId
  110. if (modelId && modelId in koduModels) {
  111. const id = modelId as KoduModelId
  112. return { id, info: koduModels[id] }
  113. }
  114. return { id: koduDefaultModelId, info: koduModels[koduDefaultModelId] }
  115. }
  116. }