prompt.go 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. package app
  2. import (
  3. "errors"
  4. "time"
  5. "github.com/sst/opencode-sdk-go"
  6. "github.com/sst/opencode/internal/attachment"
  7. "github.com/sst/opencode/internal/id"
  8. )
  9. type Prompt struct {
  10. Text string `toml:"text"`
  11. Attachments []*attachment.Attachment `toml:"attachments"`
  12. }
  13. func (p Prompt) ToMessage(
  14. messageID string,
  15. sessionID string,
  16. ) Message {
  17. message := opencode.UserMessage{
  18. ID: messageID,
  19. SessionID: sessionID,
  20. Role: opencode.UserMessageRoleUser,
  21. Time: opencode.UserMessageTime{
  22. Created: float64(time.Now().UnixMilli()),
  23. },
  24. }
  25. text := p.Text
  26. textAttachments := []*attachment.Attachment{}
  27. for _, attachment := range p.Attachments {
  28. if attachment.Type == "text" {
  29. textAttachments = append(textAttachments, attachment)
  30. }
  31. }
  32. for i := 0; i < len(textAttachments)-1; i++ {
  33. for j := i + 1; j < len(textAttachments); j++ {
  34. if textAttachments[i].StartIndex < textAttachments[j].StartIndex {
  35. textAttachments[i], textAttachments[j] = textAttachments[j], textAttachments[i]
  36. }
  37. }
  38. }
  39. for _, att := range textAttachments {
  40. if source, ok := att.GetTextSource(); ok {
  41. text = text[:att.StartIndex] + source.Value + text[att.EndIndex:]
  42. }
  43. }
  44. parts := []opencode.PartUnion{opencode.TextPart{
  45. ID: id.Ascending(id.Part),
  46. MessageID: messageID,
  47. SessionID: sessionID,
  48. Type: opencode.TextPartTypeText,
  49. Text: text,
  50. }}
  51. for _, attachment := range p.Attachments {
  52. if attachment.Type == "agent" {
  53. source, _ := attachment.GetAgentSource()
  54. parts = append(parts, opencode.AgentPart{
  55. ID: id.Ascending(id.Part),
  56. MessageID: messageID,
  57. SessionID: sessionID,
  58. Name: source.Name,
  59. Source: opencode.AgentPartSource{
  60. Value: attachment.Display,
  61. Start: int64(attachment.StartIndex),
  62. End: int64(attachment.EndIndex),
  63. },
  64. })
  65. continue
  66. }
  67. text := opencode.FilePartSourceText{
  68. Start: int64(attachment.StartIndex),
  69. End: int64(attachment.EndIndex),
  70. Value: attachment.Display,
  71. }
  72. source := &opencode.FilePartSource{}
  73. switch attachment.Type {
  74. case "text":
  75. continue
  76. case "file":
  77. if fileSource, ok := attachment.GetFileSource(); ok {
  78. source = &opencode.FilePartSource{
  79. Text: text,
  80. Path: fileSource.Path,
  81. Type: opencode.FilePartSourceTypeFile,
  82. }
  83. }
  84. case "symbol":
  85. if symbolSource, ok := attachment.GetSymbolSource(); ok {
  86. source = &opencode.FilePartSource{
  87. Text: text,
  88. Path: symbolSource.Path,
  89. Type: opencode.FilePartSourceTypeSymbol,
  90. Kind: int64(symbolSource.Kind),
  91. Name: symbolSource.Name,
  92. Range: opencode.SymbolSourceRange{
  93. Start: opencode.SymbolSourceRangeStart{
  94. Line: float64(symbolSource.Range.Start.Line),
  95. Character: float64(symbolSource.Range.Start.Char),
  96. },
  97. End: opencode.SymbolSourceRangeEnd{
  98. Line: float64(symbolSource.Range.End.Line),
  99. Character: float64(symbolSource.Range.End.Char),
  100. },
  101. },
  102. }
  103. }
  104. }
  105. parts = append(parts, opencode.FilePart{
  106. ID: id.Ascending(id.Part),
  107. MessageID: messageID,
  108. SessionID: sessionID,
  109. Type: opencode.FilePartTypeFile,
  110. Filename: attachment.Filename,
  111. Mime: attachment.MediaType,
  112. URL: attachment.URL,
  113. Source: *source,
  114. })
  115. }
  116. return Message{
  117. Info: message,
  118. Parts: parts,
  119. }
  120. }
  121. func (m Message) ToPrompt() (*Prompt, error) {
  122. switch m.Info.(type) {
  123. case opencode.UserMessage:
  124. text := ""
  125. attachments := []*attachment.Attachment{}
  126. for _, part := range m.Parts {
  127. switch p := part.(type) {
  128. case opencode.TextPart:
  129. if p.Synthetic {
  130. continue
  131. }
  132. text += p.Text + " "
  133. case opencode.AgentPart:
  134. attachments = append(attachments, &attachment.Attachment{
  135. ID: p.ID,
  136. Type: "agent",
  137. Display: p.Source.Value,
  138. StartIndex: int(p.Source.Start),
  139. EndIndex: int(p.Source.End),
  140. Source: &attachment.AgentSource{
  141. Name: p.Name,
  142. },
  143. })
  144. case opencode.FilePart:
  145. switch p.Source.Type {
  146. case "file":
  147. attachments = append(attachments, &attachment.Attachment{
  148. ID: p.ID,
  149. Type: "file",
  150. Display: p.Source.Text.Value,
  151. URL: p.URL,
  152. Filename: p.Filename,
  153. MediaType: p.Mime,
  154. StartIndex: int(p.Source.Text.Start),
  155. EndIndex: int(p.Source.Text.End),
  156. Source: &attachment.FileSource{
  157. Path: p.Source.Path,
  158. Mime: p.Mime,
  159. },
  160. })
  161. case "symbol":
  162. r := p.Source.Range.(opencode.SymbolSourceRange)
  163. attachments = append(attachments, &attachment.Attachment{
  164. ID: p.ID,
  165. Type: "symbol",
  166. Display: p.Source.Text.Value,
  167. URL: p.URL,
  168. Filename: p.Filename,
  169. MediaType: p.Mime,
  170. StartIndex: int(p.Source.Text.Start),
  171. EndIndex: int(p.Source.Text.End),
  172. Source: &attachment.SymbolSource{
  173. Path: p.Source.Path,
  174. Name: p.Source.Name,
  175. Kind: int(p.Source.Kind),
  176. Range: attachment.SymbolRange{
  177. Start: attachment.Position{
  178. Line: int(r.Start.Line),
  179. Char: int(r.Start.Character),
  180. },
  181. End: attachment.Position{
  182. Line: int(r.End.Line),
  183. Char: int(r.End.Character),
  184. },
  185. },
  186. },
  187. })
  188. }
  189. }
  190. }
  191. return &Prompt{
  192. Text: text,
  193. Attachments: attachments,
  194. }, nil
  195. }
  196. return nil, errors.New("unknown message type")
  197. }
  198. func (m Message) ToSessionChatParams() []opencode.SessionChatParamsPartUnion {
  199. parts := []opencode.SessionChatParamsPartUnion{}
  200. for _, part := range m.Parts {
  201. switch p := part.(type) {
  202. case opencode.TextPart:
  203. parts = append(parts, opencode.TextPartInputParam{
  204. ID: opencode.F(p.ID),
  205. Type: opencode.F(opencode.TextPartInputTypeText),
  206. Text: opencode.F(p.Text),
  207. Synthetic: opencode.F(p.Synthetic),
  208. Time: opencode.F(opencode.TextPartInputTimeParam{
  209. Start: opencode.F(p.Time.Start),
  210. End: opencode.F(p.Time.End),
  211. }),
  212. })
  213. case opencode.FilePart:
  214. var source opencode.FilePartSourceUnionParam
  215. switch p.Source.Type {
  216. case "file":
  217. source = opencode.FileSourceParam{
  218. Type: opencode.F(opencode.FileSourceTypeFile),
  219. Path: opencode.F(p.Source.Path),
  220. Text: opencode.F(opencode.FilePartSourceTextParam{
  221. Start: opencode.F(int64(p.Source.Text.Start)),
  222. End: opencode.F(int64(p.Source.Text.End)),
  223. Value: opencode.F(p.Source.Text.Value),
  224. }),
  225. }
  226. case "symbol":
  227. source = opencode.SymbolSourceParam{
  228. Type: opencode.F(opencode.SymbolSourceTypeSymbol),
  229. Path: opencode.F(p.Source.Path),
  230. Name: opencode.F(p.Source.Name),
  231. Kind: opencode.F(p.Source.Kind),
  232. Range: opencode.F(opencode.SymbolSourceRangeParam{
  233. Start: opencode.F(opencode.SymbolSourceRangeStartParam{
  234. Line: opencode.F(float64(p.Source.Range.(opencode.SymbolSourceRange).Start.Line)),
  235. Character: opencode.F(float64(p.Source.Range.(opencode.SymbolSourceRange).Start.Character)),
  236. }),
  237. End: opencode.F(opencode.SymbolSourceRangeEndParam{
  238. Line: opencode.F(float64(p.Source.Range.(opencode.SymbolSourceRange).End.Line)),
  239. Character: opencode.F(float64(p.Source.Range.(opencode.SymbolSourceRange).End.Character)),
  240. }),
  241. }),
  242. Text: opencode.F(opencode.FilePartSourceTextParam{
  243. Value: opencode.F(p.Source.Text.Value),
  244. Start: opencode.F(p.Source.Text.Start),
  245. End: opencode.F(p.Source.Text.End),
  246. }),
  247. }
  248. }
  249. parts = append(parts, opencode.FilePartInputParam{
  250. ID: opencode.F(p.ID),
  251. Type: opencode.F(opencode.FilePartInputTypeFile),
  252. Mime: opencode.F(p.Mime),
  253. URL: opencode.F(p.URL),
  254. Filename: opencode.F(p.Filename),
  255. Source: opencode.F(source),
  256. })
  257. case opencode.AgentPart:
  258. parts = append(parts, opencode.AgentPartInputParam{
  259. ID: opencode.F(p.ID),
  260. Type: opencode.F(opencode.AgentPartInputTypeAgent),
  261. Name: opencode.F(p.Name),
  262. Source: opencode.F(opencode.AgentPartInputSourceParam{
  263. Value: opencode.F(p.Source.Value),
  264. Start: opencode.F(p.Source.Start),
  265. End: opencode.F(p.Source.End),
  266. }),
  267. })
  268. }
  269. }
  270. return parts
  271. }