From 900cac5562ebad09e8acd23995feed62144a862f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Carlos=20Sans=C3=B3n?=
<57395395+csansoon@users.noreply.github.com>
Date: Mon, 16 Jun 2025 18:33:48 +0200
Subject: [PATCH] 0.7.1 - allow undefined parameters
---
package.json | 2 +-
src/compiler/base/nodes/for.test.ts | 7 +---
src/compiler/base/nodes/tags/ref.test.ts | 45 ++++++++++++----------
src/compiler/base/nodes/tags/scope.test.ts | 31 +++++++--------
src/compiler/chain.test.ts | 40 -------------------
src/compiler/compile.test.ts | 22 +++--------
src/compiler/logic/nodes/identifier.ts | 8 +---
7 files changed, 48 insertions(+), 107 deletions(-)
diff --git a/package.json b/package.json
index 8ad3cde..7be7e5d 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "promptl-ai",
- "version": "0.7.0",
+ "version": "0.7.1",
"author": "Latitude Data",
"license": "MIT",
"description": "Compiler for PromptL, the prompt language",
diff --git a/src/compiler/base/nodes/for.test.ts b/src/compiler/base/nodes/for.test.ts
index 912192a..9e0269d 100644
--- a/src/compiler/base/nodes/for.test.ts
+++ b/src/compiler/base/nodes/for.test.ts
@@ -1,5 +1,3 @@
-import CompileError from '$promptl/error/error'
-import { getExpectedError } from '$promptl/test/helpers'
import { Message, MessageContent, TextContent } from '$promptl/types'
import { describe, expect, it } from 'vitest'
@@ -71,12 +69,11 @@ describe('each loops', async () => {
const prompt1 = `{{ for elemenet in ['a', 'b', 'c'] }} {{foo = 5}} {{ endfor }} {{foo}}`
const prompt2 = `{{foo = 5}} {{ for element in ['a', 'b', 'c'] }} {{foo = 7}} {{ endfor }} {{foo}}`
const prompt3 = `{{foo = 5}} {{ for element in [1, 2, 3] }} {{foo += element}} {{ endfor }} {{foo}}`
- const action1 = () => render({ prompt: prompt1, parameters: {} })
- const error1 = await getExpectedError(action1, CompileError)
+ const result1 = await getCompiledText(prompt1)
const result2 = await getCompiledText(prompt2)
const result3 = await getCompiledText(prompt3)
- expect(error1.code).toBe('variable-not-declared')
+ expect(result1).toBe('')
expect(result2).toBe('7')
expect(result3).toBe('11')
})
diff --git a/src/compiler/base/nodes/tags/ref.test.ts b/src/compiler/base/nodes/tags/ref.test.ts
index 5cf743e..e34ad88 100644
--- a/src/compiler/base/nodes/tags/ref.test.ts
+++ b/src/compiler/base/nodes/tags/ref.test.ts
@@ -56,7 +56,8 @@ describe('ref tags', async () => {
child: 'child message',
}
- const action = () => render({ prompt: prompts['parent'], adapter: Adapters.default })
+ const action = () =>
+ render({ prompt: prompts['parent'], adapter: Adapters.default })
const error = await getExpectedError(action, CompileError)
expect(error.code).toBe('missing-reference-function')
})
@@ -83,16 +84,19 @@ describe('ref tags', async () => {
parent: '',
}
- const action = () =>
- render({
- prompt: prompts['parent'],
- parameters: { foo: 'bar' },
- referenceFn: buildReferenceFn(prompts),
- adapter: Adapters.default,
- })
-
- const error = await getExpectedError(action, CompileError)
- expect(error.code).toBe('variable-not-declared')
+ const result = await render({
+ prompt: prompts['parent'],
+ referenceFn: buildReferenceFn(prompts),
+ adapter: Adapters.default,
+ })
+ expect(result.messages.length).toBe(1)
+ const message = result.messages[0]! as SystemMessage
+ expect(message.content).toEqual([
+ {
+ type: 'text',
+ text: 'Child message:',
+ },
+ ])
})
it('referenced prompts can receive parameters as tag attributes', async () => {
@@ -336,12 +340,12 @@ describe('ref tags', async () => {
{{ bar }}
{{ endfor }}
- `)
+ `),
}
const metadata = await scan({
prompt: prompts['parent'],
- referenceFn: buildReferenceFn(prompts)
+ referenceFn: buildReferenceFn(prompts),
})
const result = await render({
@@ -351,18 +355,19 @@ describe('ref tags', async () => {
})
expect(result.messages.length).toBe(2)
- const [ firstMessage, secondMessage ] = result.messages as [UserMessage, SystemMessage]
+ const [firstMessage, secondMessage] = result.messages as [
+ UserMessage,
+ SystemMessage,
+ ]
expect(firstMessage.role).toBe(MessageRole.user)
expect(firstMessage.content.length).toBe(3)
expect(firstMessage.content).toEqual([
- { type: 'text', text: '11'},
- { type: 'text', text: '12'},
- { type: 'text', text: '13'},
+ { type: 'text', text: '11' },
+ { type: 'text', text: '12' },
+ { type: 'text', text: '13' },
])
expect(secondMessage.role).toBe(MessageRole.system)
expect(secondMessage.content.length).toBe(1)
- expect(secondMessage.content).toEqual([
- { type: 'text', text: '10' }
- ])
+ expect(secondMessage.content).toEqual([{ type: 'text', text: '10' }])
})
})
diff --git a/src/compiler/base/nodes/tags/scope.test.ts b/src/compiler/base/nodes/tags/scope.test.ts
index 679c2e2..64ca851 100644
--- a/src/compiler/base/nodes/tags/scope.test.ts
+++ b/src/compiler/base/nodes/tags/scope.test.ts
@@ -1,13 +1,7 @@
import { Adapters, Chain, render } from '$promptl/index'
import { complete } from '$promptl/compiler/test/helpers'
import { removeCommonIndent } from '$promptl/compiler/utils'
-import CompileError from '$promptl/error/error'
-import { getExpectedError } from '$promptl/test/helpers'
-import {
- MessageRole,
- SystemMessage,
- UserMessage,
-} from '$promptl/types'
+import { MessageRole, SystemMessage, UserMessage } from '$promptl/types'
import { describe, expect, it, vi } from 'vitest'
describe('scope tags', async () => {
@@ -48,7 +42,7 @@ describe('scope tags', async () => {
`)
const result = await render({ prompt, adapter: Adapters.default })
-
+
expect(result.messages.length).toBe(1)
const message = result.messages[0]! as SystemMessage
expect(message.content).toEqual([
@@ -67,18 +61,19 @@ describe('scope tags', async () => {
const prompt = removeCommonIndent(`
{{ foo = 'bar' }}
- {{ foo }}
+ {{ foo == 'bar' }}
`)
- const action = () => render({
- prompt,
- parameters: { foo: 'baz' },
- adapter: Adapters.default
- })
-
- const error = await getExpectedError(action, CompileError)
- expect(error.code).toBe('variable-not-declared')
+ const result = await render({ prompt, adapter: Adapters.default })
+ expect(result.messages.length).toBe(1)
+ const message = result.messages[0]! as SystemMessage
+ expect(message.content).toEqual([
+ {
+ type: 'text',
+ text: 'false',
+ },
+ ])
})
it('can inherit parameters from parents if explicitly passed', async () => {
@@ -91,7 +86,7 @@ describe('scope tags', async () => {
const result = await render({
prompt,
parameters: { foo: 'bar' },
- adapter: Adapters.default
+ adapter: Adapters.default,
})
expect(result.messages.length).toBe(1)
diff --git a/src/compiler/chain.test.ts b/src/compiler/chain.test.ts
index f8fef30..da53525 100644
--- a/src/compiler/chain.test.ts
+++ b/src/compiler/chain.test.ts
@@ -343,19 +343,6 @@ describe('chain', async () => {
{{foo}}
`)
- const incorrectPrompt = removeCommonIndent(`
- {{foo = 5}}
-
- {{if true}}
- {{bar = 1}}
-
- {{ bar++}}
-
- {{endif}}
-
- {{bar}}
- `)
-
const correctChain = new Chain({
prompt: correctPrompt,
adapter: Adapters.default,
@@ -371,16 +358,6 @@ describe('chain', async () => {
},
],
})
-
- const incorrectChain = new Chain({
- prompt: incorrectPrompt,
- parameters: {},
- adapter: Adapters.default,
- })
-
- const action = () => complete({ chain: incorrectChain })
- const error = await getExpectedError(action, CompileError)
- expect(error.code).toBe('variable-not-declared')
})
it('maintains the scope in for loops', async () => {
@@ -419,23 +396,6 @@ describe('chain', async () => {
})
})
- it('cannot access variables created in a loop outside its scope', async () => {
- const prompt = removeCommonIndent(`
- {{for i in [1, 2, 3]}}
- {{foo = i}}
-
- {{endfor}}
-
- {{foo}}
- `)
-
- const chain = new Chain({ prompt, adapter: Adapters.default })
-
- const action = () => complete({ chain })
- const error = await getExpectedError(action, CompileError)
- expect(error.code).toBe('variable-not-declared')
- })
-
it('maintains the scope in nested loops', async () => {
const prompt = removeCommonIndent(`
{{ foo = 0 }}
diff --git a/src/compiler/compile.test.ts b/src/compiler/compile.test.ts
index 3a0ad1e..6c14b81 100644
--- a/src/compiler/compile.test.ts
+++ b/src/compiler/compile.test.ts
@@ -173,18 +173,12 @@ describe('variable assignment', async () => {
expect(result).toBe('5')
})
- it('cannot reference undefined variables', async () => {
+ it('undefined variables do not cause an error', async () => {
const prompt = `
{{ foo }}
`
- const action = () =>
- render({
- prompt: removeCommonIndent(prompt),
- parameters: {},
- adapter: Adapters.default,
- })
- const error = await getExpectedError(action, CompileError)
- expect(error.code).toBe('variable-not-declared')
+ const result = await getCompiledText(prompt)
+ expect(result).toBe('')
})
it('parameters are available as variables in the prompt', async () => {
@@ -227,14 +221,8 @@ describe('variable assignment', async () => {
{{ endif }}
{{ foo }}
`
- const action = () =>
- render({
- prompt: removeCommonIndent(prompt),
- parameters: {},
- adapter: Adapters.default,
- })
- const error = await getExpectedError(action, CompileError)
- expect(error.code).toBe('variable-not-declared')
+ const result = await getCompiledText(prompt)
+ expect(result).toBe('')
})
it('variables can be modified from an inner scope', async () => {
diff --git a/src/compiler/logic/nodes/identifier.ts b/src/compiler/logic/nodes/identifier.ts
index 33bd632..bd712a2 100644
--- a/src/compiler/logic/nodes/identifier.ts
+++ b/src/compiler/logic/nodes/identifier.ts
@@ -9,13 +9,9 @@ import type { Identifier } from 'estree'
* ### Identifier
* Represents a variable from the scope.
*/
-export async function resolve({
- node,
- scope,
- raiseError,
-}: ResolveNodeProps) {
+export async function resolve({ node, scope }: ResolveNodeProps) {
if (!scope.exists(node.name)) {
- raiseError(errors.variableNotDeclared(node.name), node)
+ return undefined
}
return scope.get(node.name)
}