@milkdown/crepe 7.19.2 → 7.21.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.
Files changed (209) hide show
  1. package/lib/cjs/builder.js +44 -1
  2. package/lib/cjs/builder.js.map +1 -1
  3. package/lib/cjs/feature/ai/index.js +1492 -0
  4. package/lib/cjs/feature/ai/index.js.map +1 -0
  5. package/lib/cjs/feature/block-edit/index.js +9 -2
  6. package/lib/cjs/feature/block-edit/index.js.map +1 -1
  7. package/lib/cjs/feature/code-mirror/index.js +2 -0
  8. package/lib/cjs/feature/code-mirror/index.js.map +1 -1
  9. package/lib/cjs/feature/cursor/index.js +2 -0
  10. package/lib/cjs/feature/cursor/index.js.map +1 -1
  11. package/lib/cjs/feature/image-block/index.js +5 -1
  12. package/lib/cjs/feature/image-block/index.js.map +1 -1
  13. package/lib/cjs/feature/latex/index.js +7 -0
  14. package/lib/cjs/feature/latex/index.js.map +1 -1
  15. package/lib/cjs/feature/link-tooltip/index.js +2 -0
  16. package/lib/cjs/feature/link-tooltip/index.js.map +1 -1
  17. package/lib/cjs/feature/list-item/index.js +2 -0
  18. package/lib/cjs/feature/list-item/index.js.map +1 -1
  19. package/lib/cjs/feature/placeholder/index.js +2 -0
  20. package/lib/cjs/feature/placeholder/index.js.map +1 -1
  21. package/lib/cjs/feature/table/index.js +2 -0
  22. package/lib/cjs/feature/table/index.js.map +1 -1
  23. package/lib/cjs/feature/toolbar/index.js +497 -5
  24. package/lib/cjs/feature/toolbar/index.js.map +1 -1
  25. package/lib/cjs/feature/top-bar/index.js +791 -0
  26. package/lib/cjs/feature/top-bar/index.js.map +1 -0
  27. package/lib/cjs/index.js +2047 -160
  28. package/lib/cjs/index.js.map +1 -1
  29. package/lib/cjs/llm-providers/anthropic/index.js +147 -0
  30. package/lib/cjs/llm-providers/anthropic/index.js.map +1 -0
  31. package/lib/cjs/llm-providers/openai/index.js +138 -0
  32. package/lib/cjs/llm-providers/openai/index.js.map +1 -0
  33. package/lib/esm/builder.js +44 -1
  34. package/lib/esm/builder.js.map +1 -1
  35. package/lib/esm/feature/ai/index.js +1487 -0
  36. package/lib/esm/feature/ai/index.js.map +1 -0
  37. package/lib/esm/feature/block-edit/index.js +9 -2
  38. package/lib/esm/feature/block-edit/index.js.map +1 -1
  39. package/lib/esm/feature/code-mirror/index.js +2 -0
  40. package/lib/esm/feature/code-mirror/index.js.map +1 -1
  41. package/lib/esm/feature/cursor/index.js +2 -0
  42. package/lib/esm/feature/cursor/index.js.map +1 -1
  43. package/lib/esm/feature/image-block/index.js +5 -1
  44. package/lib/esm/feature/image-block/index.js.map +1 -1
  45. package/lib/esm/feature/latex/index.js +7 -0
  46. package/lib/esm/feature/latex/index.js.map +1 -1
  47. package/lib/esm/feature/link-tooltip/index.js +2 -0
  48. package/lib/esm/feature/link-tooltip/index.js.map +1 -1
  49. package/lib/esm/feature/list-item/index.js +2 -0
  50. package/lib/esm/feature/list-item/index.js.map +1 -1
  51. package/lib/esm/feature/placeholder/index.js +2 -0
  52. package/lib/esm/feature/placeholder/index.js.map +1 -1
  53. package/lib/esm/feature/table/index.js +2 -0
  54. package/lib/esm/feature/table/index.js.map +1 -1
  55. package/lib/esm/feature/toolbar/index.js +499 -7
  56. package/lib/esm/feature/toolbar/index.js.map +1 -1
  57. package/lib/esm/feature/top-bar/index.js +789 -0
  58. package/lib/esm/feature/top-bar/index.js.map +1 -0
  59. package/lib/esm/index.js +2040 -153
  60. package/lib/esm/index.js.map +1 -1
  61. package/lib/esm/llm-providers/anthropic/index.js +145 -0
  62. package/lib/esm/llm-providers/anthropic/index.js.map +1 -0
  63. package/lib/esm/llm-providers/openai/index.js +136 -0
  64. package/lib/esm/llm-providers/openai/index.js.map +1 -0
  65. package/lib/theme/common/ai.css +446 -0
  66. package/lib/theme/common/code-mirror.css +14 -0
  67. package/lib/theme/common/diff.css +177 -0
  68. package/lib/theme/common/style.css +3 -0
  69. package/lib/theme/common/top-bar.css +152 -0
  70. package/lib/tsconfig.tsbuildinfo +1 -1
  71. package/lib/types/core/builder.d.ts +2 -1
  72. package/lib/types/core/builder.d.ts.map +1 -1
  73. package/lib/types/feature/ai/ai.spec.d.ts +2 -0
  74. package/lib/types/feature/ai/ai.spec.d.ts.map +1 -0
  75. package/lib/types/feature/ai/commands.d.ts +24 -0
  76. package/lib/types/feature/ai/commands.d.ts.map +1 -0
  77. package/lib/types/feature/ai/context.d.ts +4 -0
  78. package/lib/types/feature/ai/context.d.ts.map +1 -0
  79. package/lib/types/feature/ai/diff-actions/index.d.ts +12 -0
  80. package/lib/types/feature/ai/diff-actions/index.d.ts.map +1 -0
  81. package/lib/types/feature/ai/diff-actions/view.d.ts +21 -0
  82. package/lib/types/feature/ai/diff-actions/view.d.ts.map +1 -0
  83. package/lib/types/feature/ai/index.d.ts +7 -0
  84. package/lib/types/feature/ai/index.d.ts.map +1 -0
  85. package/lib/types/feature/ai/instruction-tooltip/component.d.ts +26 -0
  86. package/lib/types/feature/ai/instruction-tooltip/component.d.ts.map +1 -0
  87. package/lib/types/feature/ai/instruction-tooltip/index.d.ts +17 -0
  88. package/lib/types/feature/ai/instruction-tooltip/index.d.ts.map +1 -0
  89. package/lib/types/feature/ai/instruction-tooltip/suggestions.d.ts +50 -0
  90. package/lib/types/feature/ai/instruction-tooltip/suggestions.d.ts.map +1 -0
  91. package/lib/types/feature/ai/instruction-tooltip/view.d.ts +19 -0
  92. package/lib/types/feature/ai/instruction-tooltip/view.d.ts.map +1 -0
  93. package/lib/types/feature/ai/streaming-indicator.d.ts +9 -0
  94. package/lib/types/feature/ai/streaming-indicator.d.ts.map +1 -0
  95. package/lib/types/feature/ai/types.d.ts +58 -0
  96. package/lib/types/feature/ai/types.d.ts.map +1 -0
  97. package/lib/types/feature/block-edit/handle/component.d.ts.map +1 -1
  98. package/lib/types/feature/block-edit/menu/component.d.ts.map +1 -1
  99. package/lib/types/feature/image-block/index.d.ts +2 -0
  100. package/lib/types/feature/image-block/index.d.ts.map +1 -1
  101. package/lib/types/feature/index.d.ts +7 -1
  102. package/lib/types/feature/index.d.ts.map +1 -1
  103. package/lib/types/feature/latex/inline-tooltip/component.d.ts.map +1 -1
  104. package/lib/types/feature/latex/inline-tooltip/inline-tooltip.spec.d.ts +2 -0
  105. package/lib/types/feature/latex/inline-tooltip/inline-tooltip.spec.d.ts.map +1 -0
  106. package/lib/types/feature/latex/inline-tooltip/view.d.ts.map +1 -1
  107. package/lib/types/feature/loader.d.ts.map +1 -1
  108. package/lib/types/feature/toolbar/component.d.ts.map +1 -1
  109. package/lib/types/feature/toolbar/config.d.ts +1 -1
  110. package/lib/types/feature/toolbar/config.d.ts.map +1 -1
  111. package/lib/types/feature/toolbar/index.d.ts +1 -0
  112. package/lib/types/feature/toolbar/index.d.ts.map +1 -1
  113. package/lib/types/feature/top-bar/component.d.ts +11 -0
  114. package/lib/types/feature/top-bar/component.d.ts.map +1 -0
  115. package/lib/types/feature/top-bar/config.d.ts +34 -0
  116. package/lib/types/feature/top-bar/config.d.ts.map +1 -0
  117. package/lib/types/feature/top-bar/index.d.ts +26 -0
  118. package/lib/types/feature/top-bar/index.d.ts.map +1 -0
  119. package/lib/types/icons/ai.d.ts +2 -0
  120. package/lib/types/icons/ai.d.ts.map +1 -0
  121. package/lib/types/icons/chevron-left.d.ts +2 -0
  122. package/lib/types/icons/chevron-left.d.ts.map +1 -0
  123. package/lib/types/icons/chevron-right.d.ts +2 -0
  124. package/lib/types/icons/chevron-right.d.ts.map +1 -0
  125. package/lib/types/icons/code-block.d.ts +2 -0
  126. package/lib/types/icons/code-block.d.ts.map +1 -0
  127. package/lib/types/icons/enter-key.d.ts +2 -0
  128. package/lib/types/icons/enter-key.d.ts.map +1 -0
  129. package/lib/types/icons/grammar-check.d.ts +2 -0
  130. package/lib/types/icons/grammar-check.d.ts.map +1 -0
  131. package/lib/types/icons/index.d.ts +12 -0
  132. package/lib/types/icons/index.d.ts.map +1 -1
  133. package/lib/types/icons/longer.d.ts +2 -0
  134. package/lib/types/icons/longer.d.ts.map +1 -0
  135. package/lib/types/icons/retry.d.ts +2 -0
  136. package/lib/types/icons/retry.d.ts.map +1 -0
  137. package/lib/types/icons/send-prompt.d.ts +2 -0
  138. package/lib/types/icons/send-prompt.d.ts.map +1 -0
  139. package/lib/types/icons/send.d.ts +2 -0
  140. package/lib/types/icons/send.d.ts.map +1 -0
  141. package/lib/types/icons/shorter.d.ts +2 -0
  142. package/lib/types/icons/shorter.d.ts.map +1 -0
  143. package/lib/types/icons/translate.d.ts +2 -0
  144. package/lib/types/icons/translate.d.ts.map +1 -0
  145. package/lib/types/llm-providers/anthropic/index.d.ts +21 -0
  146. package/lib/types/llm-providers/anthropic/index.d.ts.map +1 -0
  147. package/lib/types/llm-providers/openai/index.d.ts +15 -0
  148. package/lib/types/llm-providers/openai/index.d.ts.map +1 -0
  149. package/lib/types/llm-providers/providers.spec.d.ts +2 -0
  150. package/lib/types/llm-providers/providers.spec.d.ts.map +1 -0
  151. package/lib/types/llm-providers/shared.d.ts +16 -0
  152. package/lib/types/llm-providers/shared.d.ts.map +1 -0
  153. package/lib/types/utils/group-builder.d.ts +1 -1
  154. package/lib/types/utils/group-builder.d.ts.map +1 -1
  155. package/lib/types/utils/keep-alive.d.ts +2 -0
  156. package/lib/types/utils/keep-alive.d.ts.map +1 -0
  157. package/package.json +34 -13
  158. package/src/core/builder.ts +39 -2
  159. package/src/feature/ai/ai.spec.ts +742 -0
  160. package/src/feature/ai/commands.ts +257 -0
  161. package/src/feature/ai/context.ts +45 -0
  162. package/src/feature/ai/diff-actions/index.ts +95 -0
  163. package/src/feature/ai/diff-actions/view.ts +237 -0
  164. package/src/feature/ai/index.ts +118 -0
  165. package/src/feature/ai/instruction-tooltip/component.tsx +414 -0
  166. package/src/feature/ai/instruction-tooltip/index.ts +101 -0
  167. package/src/feature/ai/instruction-tooltip/suggestions.ts +249 -0
  168. package/src/feature/ai/instruction-tooltip/view.ts +159 -0
  169. package/src/feature/ai/streaming-indicator.ts +183 -0
  170. package/src/feature/ai/types.ts +178 -0
  171. package/src/feature/block-edit/handle/component.tsx +3 -2
  172. package/src/feature/block-edit/menu/component.tsx +3 -2
  173. package/src/feature/block-edit/menu/config.ts +1 -1
  174. package/src/feature/image-block/index.ts +4 -0
  175. package/src/feature/index.ts +14 -2
  176. package/src/feature/latex/inline-tooltip/component.tsx +4 -2
  177. package/src/feature/latex/inline-tooltip/inline-tooltip.spec.ts +81 -0
  178. package/src/feature/latex/inline-tooltip/view.ts +2 -0
  179. package/src/feature/loader.ts +8 -0
  180. package/src/feature/toolbar/component.tsx +7 -5
  181. package/src/feature/toolbar/config.ts +27 -1
  182. package/src/feature/toolbar/index.ts +1 -0
  183. package/src/feature/top-bar/component.tsx +198 -0
  184. package/src/feature/top-bar/config.ts +367 -0
  185. package/src/feature/top-bar/index.ts +113 -0
  186. package/src/icons/ai.ts +14 -0
  187. package/src/icons/chevron-left.ts +15 -0
  188. package/src/icons/chevron-right.ts +15 -0
  189. package/src/icons/code-block.ts +12 -0
  190. package/src/icons/enter-key.ts +13 -0
  191. package/src/icons/grammar-check.ts +13 -0
  192. package/src/icons/index.ts +12 -0
  193. package/src/icons/longer.ts +13 -0
  194. package/src/icons/retry.ts +13 -0
  195. package/src/icons/send-prompt.ts +13 -0
  196. package/src/icons/send.ts +13 -0
  197. package/src/icons/shorter.ts +13 -0
  198. package/src/icons/translate.ts +13 -0
  199. package/src/llm-providers/anthropic/index.ts +132 -0
  200. package/src/llm-providers/openai/index.ts +109 -0
  201. package/src/llm-providers/providers.spec.ts +472 -0
  202. package/src/llm-providers/shared.ts +160 -0
  203. package/src/theme/common/ai.css +430 -0
  204. package/src/theme/common/code-mirror.css +14 -0
  205. package/src/theme/common/diff.css +196 -0
  206. package/src/theme/common/style.css +3 -0
  207. package/src/theme/common/top-bar.css +156 -0
  208. package/src/utils/group-builder.ts +1 -1
  209. package/src/utils/keep-alive.ts +3 -0
@@ -0,0 +1,15 @@
1
+ export const chevronRightIcon = `
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ fill="none"
5
+ viewBox="0 0 24 24"
6
+ stroke-width="1.5"
7
+ stroke="currentColor"
8
+ >
9
+ <path
10
+ stroke-linecap="round"
11
+ stroke-linejoin="round"
12
+ d="M8.25 4.5l7.5 7.5-7.5 7.5"
13
+ />
14
+ </svg>
15
+ `
@@ -0,0 +1,12 @@
1
+ export const codeBlockIcon = `
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ width="24"
5
+ height="24"
6
+ viewBox="0 0 24 24"
7
+ >
8
+ <path
9
+ d="M3 3h18a1 1 0 0 1 1 1v16a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V4a1 1 0 0 1 1-1zm1 2v14h16V5H4zm8 10h6v2h-6v-2zm-3.333-3L5.838 9.172l1.415-1.415L11.495 12l-4.242 4.243-1.415-1.415L8.667 12z"
10
+ />
11
+ </svg>
12
+ `
@@ -0,0 +1,13 @@
1
+ export const enterKeyIcon = `
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ width="24"
5
+ height="24"
6
+ viewBox="0 0 24 24"
7
+ >
8
+ <path
9
+ fill="currentColor"
10
+ d="M9 16l-5-5l5-5l1.4 1.4L7.825 10H18V6h2v6H7.825l2.575 2.6z"
11
+ />
12
+ </svg>
13
+ `
@@ -0,0 +1,13 @@
1
+ export const grammarCheckIcon = `
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ width="24"
5
+ height="24"
6
+ viewBox="0 0 24 24"
7
+ >
8
+ <path
9
+ fill="currentColor"
10
+ d="M9.55 17.575L4.225 12.25l1.4-1.425L9.55 14.75l8.825-8.825l1.4 1.425z"
11
+ />
12
+ </svg>
13
+ `
@@ -1,3 +1,4 @@
1
+ export * from './ai'
1
2
  export * from './align-center'
2
3
  export * from './align-left'
3
4
  export * from './align-right'
@@ -8,13 +9,18 @@ export * from './caption'
8
9
  export * from './check-box-checked'
9
10
  export * from './check-box-unchecked'
10
11
  export * from './chevron-down'
12
+ export * from './chevron-left'
13
+ export * from './chevron-right'
11
14
  export * from './clear'
12
15
  export * from './code'
16
+ export * from './code-block'
13
17
  export * from './confirm'
14
18
  export * from './copy'
15
19
  export * from './divider'
16
20
  export * from './drag-handle'
17
21
  export * from './edit'
22
+ export * from './enter-key'
23
+ export * from './grammar-check'
18
24
  export * from './h1'
19
25
  export * from './h2'
20
26
  export * from './h3'
@@ -24,15 +30,21 @@ export * from './h6'
24
30
  export * from './image'
25
31
  export * from './italic'
26
32
  export * from './link'
33
+ export * from './longer'
27
34
  export * from './menu'
28
35
  export * from './ordered-list'
29
36
  export * from './plus'
30
37
  export * from './quote'
31
38
  export * from './remove'
39
+ export * from './retry'
32
40
  export * from './search'
41
+ export * from './send'
42
+ export * from './send-prompt'
43
+ export * from './shorter'
33
44
  export * from './strikethrough'
34
45
  export * from './table'
35
46
  export * from './text'
36
47
  export * from './todo-list'
48
+ export * from './translate'
37
49
  export * from './functions'
38
50
  export * from './visibility-off'
@@ -0,0 +1,13 @@
1
+ export const longerIcon = `
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ width="24"
5
+ height="24"
6
+ viewBox="0 0 24 24"
7
+ >
8
+ <path
9
+ fill="currentColor"
10
+ d="M3 18v-2h13v2zm0-5v-2h18v2zm0-5V6h18v2zm15 13v-3h-3v-2h3v-3h2v3h3v2h-3v3z"
11
+ />
12
+ </svg>
13
+ `
@@ -0,0 +1,13 @@
1
+ export const retryIcon = `
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ width="24"
5
+ height="24"
6
+ viewBox="0 0 24 24"
7
+ >
8
+ <path
9
+ fill="currentColor"
10
+ d="M12 20q-3.35 0-5.675-2.325T4 12q0-3.35 2.325-5.675T12 4q1.725 0 3.3.713T18 6.75V4h2v7h-7V9h4.2q-.8-1.4-2.187-2.2T12 6Q9.5 6 7.75 7.75T6 12q0 2.5 1.75 4.25T12 18q1.925 0 3.475-1.1T17.65 14h2.1q-.7 2.65-2.85 4.325T12 20"
11
+ />
12
+ </svg>
13
+ `
@@ -0,0 +1,13 @@
1
+ export const sendPromptIcon = `
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ width="24"
5
+ height="24"
6
+ viewBox="0 0 24 24"
7
+ >
8
+ <path
9
+ fill="currentColor"
10
+ d="M3 20v-6l8-2l-8-2V4l19 8z"
11
+ />
12
+ </svg>
13
+ `
@@ -0,0 +1,13 @@
1
+ export const sendIcon = `
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ width="24"
5
+ height="24"
6
+ viewBox="0 0 24 24"
7
+ >
8
+ <path
9
+ fill="currentColor"
10
+ d="M11 20V7.825l-5.6 5.6L4 12l8-8l8 8l-1.4 1.425l-5.6-5.6V20z"
11
+ />
12
+ </svg>
13
+ `
@@ -0,0 +1,13 @@
1
+ export const shorterIcon = `
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ width="24"
5
+ height="24"
6
+ viewBox="0 0 24 24"
7
+ >
8
+ <path
9
+ fill="currentColor"
10
+ d="M5 13v-2h14v2z"
11
+ />
12
+ </svg>
13
+ `
@@ -0,0 +1,13 @@
1
+ export const translateIcon = `
2
+ <svg
3
+ xmlns="http://www.w3.org/2000/svg"
4
+ width="24"
5
+ height="24"
6
+ viewBox="0 0 24 24"
7
+ >
8
+ <path
9
+ fill="currentColor"
10
+ d="m12.9 16l-2.55-2.5l.05-.05q1.45-1.6 2.262-3.117T13.95 7H16V5h-6V3H8v2H2v2h9.1q-.5 1.2-1.225 2.387T8 11.7q-.6-.6-1.187-1.412T5.85 8.6H3.85q.5 1.4 1.3 2.7T6.95 13.7L2.85 17.75L4.25 19.15L8 15.4L10.55 17.95zM18.5 22h-2L21 10h2l4.5 12h-2l-1.05-3h-4.9zm1.6-5h3.6l-1.8-5.1z"
11
+ />
12
+ </svg>
13
+ `
@@ -0,0 +1,132 @@
1
+ import type { AIPromptContext, AIProvider } from '../../feature/ai/types'
2
+
3
+ import {
4
+ type BaseProviderConfig,
5
+ assertBrowserSafe,
6
+ buildDefaultUserMessage,
7
+ parseSSE,
8
+ readErrorBody,
9
+ resolveSystemPrompt,
10
+ } from '../shared'
11
+
12
+ const PROVIDER_NAME = 'milkdown/providers/anthropic'
13
+ const DEFAULT_BASE_URL = 'https://api.anthropic.com'
14
+ const DEFAULT_ANTHROPIC_VERSION = '2023-06-01'
15
+ const DEFAULT_MAX_TOKENS = 4096
16
+
17
+ export interface AnthropicMessage {
18
+ role: 'user' | 'assistant'
19
+ content: string
20
+ }
21
+
22
+ export interface AnthropicBuildMessagesResult {
23
+ /// Anthropic puts the system prompt in a top-level `system` field,
24
+ /// not in the messages array.
25
+ system?: string
26
+ messages: AnthropicMessage[]
27
+ }
28
+
29
+ export interface AnthropicProviderConfig extends BaseProviderConfig {
30
+ /// Anthropic's API requires `max_tokens`. Defaults to 4096.
31
+ maxTokens?: number
32
+
33
+ /// `anthropic-version` header. Defaults to `2023-06-01`.
34
+ anthropicVersion?: string
35
+
36
+ /// Customize the request payload. The default builder maps the
37
+ /// resolved system prompt to `system` and produces a single user
38
+ /// message containing the document, selection, and instruction.
39
+ buildMessages?: (
40
+ context: AIPromptContext,
41
+ defaults: { systemPrompt: string | null; userMessage: string }
42
+ ) => AnthropicBuildMessagesResult
43
+
44
+ /// Extra fields merged into the request body (e.g. `temperature`,
45
+ /// `top_p`, `metadata`, `tool_choice`).
46
+ body?: Record<string, unknown>
47
+ }
48
+
49
+ interface AnthropicStreamEvent {
50
+ type: string
51
+ delta?: {
52
+ type?: string
53
+ text?: string
54
+ }
55
+ }
56
+
57
+ /// Build an `AIProvider` backed by Anthropic's `/v1/messages` streaming
58
+ /// endpoint. The returned function plugs directly into
59
+ /// `Crepe.Feature.AI`'s `provider` field.
60
+ export function createAnthropicProvider(
61
+ config: AnthropicProviderConfig
62
+ ): AIProvider {
63
+ assertBrowserSafe(config, PROVIDER_NAME)
64
+
65
+ return async function* anthropicProvider(context, signal) {
66
+ const systemPrompt = resolveSystemPrompt(config.systemPrompt)
67
+ const userMessage = buildDefaultUserMessage(context)
68
+
69
+ const built = config.buildMessages
70
+ ? config.buildMessages(context, { systemPrompt, userMessage })
71
+ : {
72
+ system: systemPrompt ?? undefined,
73
+ messages: [
74
+ { role: 'user' as const, content: userMessage },
75
+ ] satisfies AnthropicMessage[],
76
+ }
77
+
78
+ const baseURL = (config.baseURL ?? DEFAULT_BASE_URL).replace(/\/+$/, '')
79
+ const url = `${baseURL}/v1/messages`
80
+
81
+ const headers: Record<string, string> = {
82
+ 'Content-Type': 'application/json',
83
+ 'anthropic-version': config.anthropicVersion ?? DEFAULT_ANTHROPIC_VERSION,
84
+ ...(config.apiKey ? { 'x-api-key': config.apiKey } : {}),
85
+ // Required for direct browser → api.anthropic.com calls; harmless
86
+ // when proxying through a backend.
87
+ ...(config.dangerouslyAllowBrowser
88
+ ? { 'anthropic-dangerous-direct-browser-access': 'true' }
89
+ : {}),
90
+ ...config.headers,
91
+ }
92
+
93
+ const body = JSON.stringify({
94
+ model: config.model,
95
+ max_tokens: config.maxTokens ?? DEFAULT_MAX_TOKENS,
96
+ stream: true,
97
+ // Caller-supplied empty string is still a value; only `undefined`
98
+ // (i.e. resolveSystemPrompt returned `null`) means "omit".
99
+ ...(built.system !== undefined ? { system: built.system } : {}),
100
+ messages: built.messages,
101
+ ...config.body,
102
+ })
103
+
104
+ const response = await fetch(url, {
105
+ method: 'POST',
106
+ headers,
107
+ body,
108
+ signal,
109
+ })
110
+
111
+ if (!response.ok) {
112
+ throw await readErrorBody(response, PROVIDER_NAME)
113
+ }
114
+
115
+ for await (const payload of parseSSE(response, signal)) {
116
+ let event: AnthropicStreamEvent
117
+ try {
118
+ event = JSON.parse(payload) as AnthropicStreamEvent
119
+ } catch {
120
+ continue
121
+ }
122
+ if (
123
+ event.type === 'content_block_delta' &&
124
+ event.delta?.type === 'text_delta' &&
125
+ event.delta.text
126
+ ) {
127
+ yield event.delta.text
128
+ }
129
+ if (event.type === 'message_stop') return
130
+ }
131
+ }
132
+ }
@@ -0,0 +1,109 @@
1
+ import type { AIPromptContext, AIProvider } from '../../feature/ai/types'
2
+
3
+ import {
4
+ type BaseProviderConfig,
5
+ assertBrowserSafe,
6
+ buildDefaultUserMessage,
7
+ parseSSE,
8
+ readErrorBody,
9
+ resolveSystemPrompt,
10
+ } from '../shared'
11
+
12
+ const PROVIDER_NAME = 'milkdown/providers/openai'
13
+ const DEFAULT_BASE_URL = 'https://api.openai.com'
14
+
15
+ export interface OpenAIChatMessage {
16
+ role: 'system' | 'user' | 'assistant'
17
+ content: string
18
+ }
19
+
20
+ export interface OpenAIProviderConfig extends BaseProviderConfig {
21
+ /// Customize the messages sent to `/v1/chat/completions`. The default
22
+ /// builder produces a system message (when not disabled) followed by
23
+ /// a structured user message containing the document, selection, and
24
+ /// instruction. The defaults are passed in so callers can wrap them
25
+ /// rather than re-deriving from scratch.
26
+ buildMessages?: (
27
+ context: AIPromptContext,
28
+ defaults: { systemPrompt: string | null; userMessage: string }
29
+ ) => OpenAIChatMessage[]
30
+
31
+ /// Extra fields merged into the request body (e.g. `temperature`,
32
+ /// `top_p`, `presence_penalty`, `response_format`).
33
+ body?: Record<string, unknown>
34
+ }
35
+
36
+ interface OpenAIStreamChunk {
37
+ choices?: Array<{
38
+ delta?: { content?: string }
39
+ finish_reason?: string | null
40
+ }>
41
+ }
42
+
43
+ /// Build an `AIProvider` backed by OpenAI's `/v1/chat/completions`
44
+ /// streaming endpoint. The returned function plugs directly into
45
+ /// `Crepe.Feature.AI`'s `provider` field.
46
+ export function createOpenAIProvider(config: OpenAIProviderConfig): AIProvider {
47
+ assertBrowserSafe(config, PROVIDER_NAME)
48
+
49
+ return async function* openAIProvider(context, signal) {
50
+ const systemPrompt = resolveSystemPrompt(config.systemPrompt)
51
+ const userMessage = buildDefaultUserMessage(context)
52
+
53
+ const messages = config.buildMessages
54
+ ? config.buildMessages(context, { systemPrompt, userMessage })
55
+ : defaultMessages(systemPrompt, userMessage)
56
+
57
+ const baseURL = (config.baseURL ?? DEFAULT_BASE_URL).replace(/\/+$/, '')
58
+ const url = `${baseURL}/v1/chat/completions`
59
+
60
+ const headers: Record<string, string> = {
61
+ 'Content-Type': 'application/json',
62
+ ...(config.apiKey ? { Authorization: `Bearer ${config.apiKey}` } : {}),
63
+ ...config.headers,
64
+ }
65
+
66
+ const body = JSON.stringify({
67
+ model: config.model,
68
+ stream: true,
69
+ messages,
70
+ ...config.body,
71
+ })
72
+
73
+ const response = await fetch(url, {
74
+ method: 'POST',
75
+ headers,
76
+ body,
77
+ signal,
78
+ })
79
+
80
+ if (!response.ok) {
81
+ throw await readErrorBody(response, PROVIDER_NAME)
82
+ }
83
+
84
+ for await (const payload of parseSSE(response, signal)) {
85
+ if (payload === '[DONE]') return
86
+ let event: OpenAIStreamChunk
87
+ try {
88
+ event = JSON.parse(payload) as OpenAIStreamChunk
89
+ } catch {
90
+ continue
91
+ }
92
+ const delta = event.choices?.[0]?.delta?.content
93
+ if (delta) yield delta
94
+ }
95
+ }
96
+ }
97
+
98
+ function defaultMessages(
99
+ systemPrompt: string | null,
100
+ userMessage: string
101
+ ): OpenAIChatMessage[] {
102
+ const messages: OpenAIChatMessage[] = []
103
+ // `null` means "omit"; an empty string is still a caller-provided
104
+ // value and should be sent as-is.
105
+ if (systemPrompt !== null)
106
+ messages.push({ role: 'system', content: systemPrompt })
107
+ messages.push({ role: 'user', content: userMessage })
108
+ return messages
109
+ }