Edit functions do not support return values

There appears to be no way to author a function that causes mutations that will return a value. Consider the use case below:

@Query({ apiName: "upsertState" })
    public async upsertState(
        plan?: string,
        forward: boolean = true,
        executionId?: string,
        inputs: string = '{}',
        xreason: string = SupportedEngines.COMS): Promise<MachineExecutions> {
        // I have to include this call or I will get an error that I am attempting to call a function not in the function registry
        // This is due to the GPT_4o being tree shaked by their algorithm
        // I did report the issue to Palantir but they likely will not fix
        const response = GPT_4o.createChatCompletion(
            {
                messages: [
                    { role: "SYSTEM", contents: [{ text: 'You are a helpful AI assistant' }] },
                    { role: "USER", contents: [{ text: 'what is your name?' }] },
                ]
            }
        );

        const solution = {
            input: '', //not relevant for this
            id: executionId || Uuid.random(),
            plan: plan || '',
        };

        const result = await getState(solution, forward, JSON.parse(inputs), xreason as SupportedEngines);

        await this.updateMachine(solution.id,
            JSON.stringify(result.stateMachine),
            result.jsonState,
            result.logs
        );

        // IMPORTANT: there are no strong read after write gaurentees in Foundry!
        // this means have to wait a bit for edits to be applied
        // read this for more details: https://www.palantir.com/docs/foundry/functions/edits-overview#caveat-edits-and-object-search
        // I'm told that if you search by id then you will get results immediatly, but leaving the loop here just for testing.
        const machine = await new Promise<MachineExecutions>(async (resolve, reject) => {
            let machine = getMachineExecution(solution);
            if (machine) {
                console.log(`machine with ID ${machine.id} found immediatly after write`);
                return resolve(machine);
            }

            const interval = 1000; // 1 second interval
            const iterations = 5; // Run 5 times
            const timer = customTimer(interval, iterations);
            // I have no idea why but I can't import timing functions from node so I wrote my own
            for await (const tick of timer) {
                console.log(`Tick ${tick + 1}`);
                console.log(new Date().getSeconds());
                machine = getMachineExecution(solution);
                if (machine) {
                    return resolve(machine);
                }
            }

            return reject(new Error('Machine execution could not be retrieved after 10 seconds'));
        })

        return machine;
    }

    @Edits(MachineExecutions)
    @OntologyEditFunction()
    public async updateMachine(id: string, stateMachine: string, state: string, logs: string | undefined): Promise<void> {
        const solution = {
            input: '', //not relevant for this
            id: id || Uuid.random(),
            plan: '', //not relevant for this
        };

        let machine = getMachineExecution(solution);

        if (!machine) {
            machine = Objects.create().machineExecutions(solution.id);
        }

        machine.machine = stateMachine;
        machine.state = state;
        machine.logs = logs;
        machine.currentState = JSON.parse(state).value;
    }

The upsertState function is what my application needs to consume. But edits are only applied when functions back actions:
“To actually be used in an operational context, Ontology Edit Functions must be configured as an Action , known as a Function-backed Action. Configuring an Action in this way allows you to provide additional metadata, configure permissions, and access the Action in various operational interfaces. As noted in the documentation, running an Edit Function outside of an Action will not actually modify any object data.”

My only workaround is to use fetch to invoke the edit action via an API call or break apart the upsert function into multiple calls, which is not ideal. I am reading this right? Does anyone else have any ideas here? This is a very limiting restriction, if this is correct.

2 Likes

My only workaround is to use fetch to invoke the edit action via an API call

I have used this approach as well but there is the risk of timeout in waiting for the results to show up.

break apart the upsert function into multiple calls, which is not ideal

While not ideal, breaking the function could allow to have another action type resume the process via Automate.

1 Like

I’m not 100% sure I’ve understood what you’re asking, but it is possible to call an object editing function from within another function (that may be triggered by at Action itself), and get object edits.

The main limitation I understand is that you cannot have a Function that returns anything used directly as an Action backing function, because there’s nowhere for the result to go.

The following works (with obscured object names), here the ‘inner’ function editArray() returns values and edits objects. It doesn’t need the @OntologyEditFunction() decorator as that’s wrapped in the function below that calls it.

public editArray(myObj: MyObjectType): string {

myObj.stringArrayProperty = ["Foo", "Bar"];

return "Blah";

};

@OntologyEditFunction()
public async mapStringArrayObjectset(obs: ObjectSet<MyObjectType>): Promise<void> {

    const obsList: MyObjectType[] = obs.all();

    let results: string[] = await obsList.map((x:MyObjectType) => this.editArray(x));

    console.log(results); // prints ['Blah']

}
1 Like

@CodeStrap ,

This is definitely limiting, we found a workaround that is pretty hacky but does get the job done. So you can’t return a custom response from an action call but as you know you can return a response from a query call. Query calls are able to run apis though so we setup a Data Connection to our own Foundry instance and created a webhook which called the Action. Then inside the query function we called the webhook which executes the action.

Because that still doesn’t solve the issue of getting the data out we added inside the action a object creation which contains the json response we need. So the query calls the action via the webhook and then if successful, does an object search to pull the json response out.

Like I said… it’s hacky but hope this helps.

Thanks for the response. Yep, my workaround is similar, but using fetch without using webhooks. With the new data source support for native connections, you can call APIs using fetch without requiring a webhook.

Thanks for the response. Just wanted to see if anyone else had found some way to make this work without direct API calls.

Just want to say that we are also using the „do a http request workaround“… could really need a solution inside the product.

1 Like

You are correct in that ontology-edit functions must have a void return type, and the workarounds mentioned here are all valid (though admittedly awkward and painful).

We are scoping a solution to this limitation, though we have nothing to concrete to report yet and it’s unlikely that something lands soon.

4 Likes

Thanks, Marshall! Looking forward to what the team comes up with.

1 Like