TestAssistantWriteRoundTrip verifies the Stage 4 write path: fantasy.Content (with ProviderMetadata) → PartFromContent → MarshalParts → DB → ParseContent (SDK path) → ConvertMessagesWithFiles → fantasy part with ProviderOptions.
(t *testing.T)
| 1550 | // MarshalParts → DB → ParseContent (SDK path) → |
| 1551 | // ConvertMessagesWithFiles → fantasy part with ProviderOptions. |
| 1552 | func TestAssistantWriteRoundTrip(t *testing.T) { |
| 1553 | t.Parallel() |
| 1554 | |
| 1555 | original := fantasy.TextContent{ |
| 1556 | Text: "response with cache hints", |
| 1557 | ProviderMetadata: fantasy.ProviderMetadata{ |
| 1558 | "anthropic": &fantasyanthropic.ProviderCacheControlOptions{ |
| 1559 | CacheControl: fantasyanthropic.CacheControl{Type: "ephemeral"}, |
| 1560 | }, |
| 1561 | }, |
| 1562 | } |
| 1563 | |
| 1564 | // Simulate persistStep: PartFromContent → MarshalParts. |
| 1565 | sdkPart := chatprompt.PartFromContent(original) |
| 1566 | require.Equal(t, codersdk.ChatMessagePartTypeText, sdkPart.Type) |
| 1567 | require.NotNil(t, sdkPart.ProviderMetadata) |
| 1568 | |
| 1569 | raw, err := chatprompt.MarshalParts([]codersdk.ChatMessagePart{sdkPart}) |
| 1570 | require.NoError(t, err) |
| 1571 | |
| 1572 | // Read back via ParseContent (takes the new SDK path, not |
| 1573 | // the legacy fallback, because the stored format is flat). |
| 1574 | parts, err := chatprompt.ParseContent(testMsg(codersdk.ChatMessageRoleAssistant, raw)) |
| 1575 | require.NoError(t, err) |
| 1576 | require.Len(t, parts, 1) |
| 1577 | assert.Equal(t, "response with cache hints", parts[0].Text) |
| 1578 | assert.JSONEq(t, string(sdkPart.ProviderMetadata), string(parts[0].ProviderMetadata)) |
| 1579 | |
| 1580 | // Full LLM dispatch: metadata reconstructed as typed options. |
| 1581 | prompt, err := chatprompt.ConvertMessagesWithFiles( |
| 1582 | context.Background(), |
| 1583 | []database.ChatMessage{{ |
| 1584 | Role: database.ChatMessageRoleAssistant, |
| 1585 | Visibility: database.ChatMessageVisibilityBoth, |
| 1586 | Content: raw, |
| 1587 | }}, |
| 1588 | nil, |
| 1589 | slogtest.Make(t, nil), |
| 1590 | ) |
| 1591 | require.NoError(t, err) |
| 1592 | require.Len(t, prompt, 1) |
| 1593 | require.Len(t, prompt[0].Content, 1) |
| 1594 | |
| 1595 | textPart, ok := fantasy.AsMessagePart[fantasy.TextPart](prompt[0].Content[0]) |
| 1596 | require.True(t, ok) |
| 1597 | require.Equal(t, "response with cache hints", textPart.Text) |
| 1598 | |
| 1599 | cc := fantasyanthropic.GetCacheControl(textPart.ProviderOptions) |
| 1600 | require.NotNil(t, cc, "cache control must survive new write → new read round-trip") |
| 1601 | require.Equal(t, "ephemeral", cc.Type) |
| 1602 | } |
| 1603 | |
| 1604 | func TestStructuredToolErrorWritePreservesJSONObject(t *testing.T) { |
| 1605 | t.Parallel() |
nothing calls this directly
no test coverage detected