llama_test.go 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105
  1. package llama
  2. import (
  3. "bufio"
  4. "bytes"
  5. "strings"
  6. "testing"
  7. )
  8. // https://github.com/ollama/ollama/issues/7978
  9. const issue7978JSONSchema = `{
  10. "type": "object",
  11. "properties": {
  12. "steps": {
  13. "type": "array",
  14. "items": {
  15. "type": "object",
  16. "properties": {
  17. "explanation": { "type": "string" },
  18. "output": { "type": "string" },
  19. "nested": {
  20. "type": "object",
  21. "properties": {
  22. "deep": { "type": "string" }
  23. }
  24. }
  25. },
  26. "required": ["explanation", "output"],
  27. "additionalProperties": false
  28. }
  29. },
  30. "final_answer": { "type": "string" },
  31. "01_numbered_key": { "type": "string" },
  32. "numbers": {
  33. "type": "array",
  34. "items": { "type": "number" }
  35. },
  36. "booleans": {
  37. "type": "array",
  38. "items": { "type": "boolean" }
  39. },
  40. "mixed": {
  41. "type": "array",
  42. "items": {
  43. "oneOf": [
  44. { "type": "string" },
  45. { "type": "number" },
  46. { "type": "boolean" }
  47. ]
  48. }
  49. }
  50. },
  51. "required": ["steps", "final_answer"],
  52. "additionalProperties": false
  53. }`
  54. func TestIssue7978(t *testing.T) {
  55. g := SchemaToGrammar([]byte(issue7978JSONSchema))
  56. if g == nil {
  57. t.Fatal("failed to convert JSON schema to grammar")
  58. }
  59. t.Logf("grammar:\n%s", g)
  60. t.Log()
  61. var got string
  62. s := bufio.NewScanner(bytes.NewReader(g))
  63. for s.Scan() {
  64. line := strings.TrimSpace(s.Text())
  65. step, _, _ := strings.Cut(line, " ::= ")
  66. step = strings.TrimSpace(step)
  67. if step == "root" {
  68. got = line
  69. }
  70. }
  71. want := `root ::= "{" space steps-kv "," space final-answer-kv ( "," space ( 01-numbered-key-kv 01-numbered-key-rest | numbers-kv numbers-rest | booleans-kv booleans-rest | mixed-kv ) )? "}" space`
  72. if got != want {
  73. t.Errorf("root =\n%qwant:\n%q", got, want)
  74. }
  75. }
  76. func TestSchemaToGrammer(t *testing.T) {
  77. cases := []struct {
  78. schema string
  79. prefix []byte // nil is check as nil
  80. }{
  81. {`invalid`, nil},
  82. // Simple heuristic/smoke test
  83. {`{"type":"object"}`, []byte("root ::= object")},
  84. }
  85. for _, c := range cases {
  86. t.Run("x", func(t *testing.T) {
  87. g := SchemaToGrammar([]byte(c.schema))
  88. if c.prefix == nil && g != nil {
  89. t.Fatalf("grammar = %v, want nil", g)
  90. }
  91. if !bytes.HasPrefix(g, c.prefix) {
  92. t.Errorf("grammar = %q, want %q", g, c.prefix)
  93. }
  94. })
  95. }
  96. }