Migrar da versão 0.9 para a 1.0

O Genkit 1.0 apresenta muitos aprimoramentos de recursos que melhoram a funcionalidade geral. Ele também tem algumas mudanças importantes. Se você estiver desenvolvendo apps com o Genkit 0.9, precisará atualizar o código do app ao fazer upgrade para a versão mais recente do Genkit. Este guia descreve as mudanças mais significativas e explica como migrar seus aplicativos de forma tranquila.

APIs Beta

Estamos lançando um canal de API Beta instável e deixando as APIs de sessão, chat e cliente do Genkit na versão Beta enquanto continuamos a aprimorá-las. Mais especificamente, as seguintes funções estão no namespace beta:

  • ai.chat
  • ai.createSession
  • ai.loadSession
  • ai.currentSession
  • ai.defineFormat
  • ai.defineInterrupt

Antigo:

import { genkit } from 'genkit';
const ai = genkit({...})
const session = ai.createSession({ ... })

Novo:

import { genkit } from 'genkit/beta';
const ai = genkit({...})
const session = ai.createSession({ ... })

Antigo:

import { runFlow, streamFlow } from 'genkit/client';

Novo:

import { runFlow, streamFlow } from 'genkit/beta/client';

Apresentação do novo pacote @genkit-ai/express

Esse novo pacote contém utilitários para facilitar a criação de um servidor Express.js com o Genkit. Confira mais detalhes nesta página.

startFlowServer foi movido de parte do objeto genkit para este novo pacote @genkit-ai/express. Para usar startFlowServer, é necessário atualizar as importações.

Antigo:

const ai = genkit({ ... });
ai.startFlowServer({
  flows: [myFlow1, myFlow2],
});

Novo:

import { startFlowServer } from '@genkit-ai/express';
startFlowServer({
  flows: [myFlow1, myFlow2],
});

Mudanças nos fluxos

Há várias mudanças nos fluxos da versão 1.0:

  • ai.defineStreamingFlow foi consolidada em ai.defineFlow,
  • onFlow foi substituído por onCallGenkit.
  • run foi movido para ai.run.
  • Há mudanças no trabalho com a autenticação.

A função run para blocos de rastreamento personalizados foi movida para parte do objeto genkit. Use ai.run para invocá-la.

Antigo:

ai.defineFlow({name: 'banana'}, async (input) => {
  const step = await run('myCode', async () => {
    return 'something'
  });
})

Novo:

ai.defineFlow({name: 'banana'}, async (input) => {
  const step = await ai.run('myCode', async () => {
    return 'something'
  });
})

ai.defineStreamingFlow foi removido. Use ai.defineFlow. Além disso, streamingCallback foi movido para um campo dentro do segundo argumento da função de fluxo e agora é chamado de sendChunk.

Antigo:

const flow = ai.defineStreamingFlow({name: 'banana'}, async (input, streamingCallback) => {
  streamingCallback({chunk: 1});
})

const {stream} = await flow()
for await (const chunk of stream) {
  // ...
}

Novo:

const flow = ai.defineFlow({name: 'banana'}, async (input, {context, sendChunk}) => {
  sendChunk({chunk: 1});
})

const {stream, output} = flow.stream(input);
for await (const chunk of stream) {
  // ...
}

A autenticação do FlowAuth agora é chamada de contexto. É possível acessar a autenticação como um campo no contexto:

Antigo:

ai.defineFlow({name: 'banana'}, async (input) => {
  const auth = getFlowAuth();
  // ...
})

Novo:

ai.defineFlow({name: 'banana'}, async (input, { context }) => {
  const auth = context.auth;
})

onFlow foi movido para o pacote firebase-functions/https e renomeado como onCallGenkit. O snippet abaixo mostra um exemplo de como usá-la.

Antigo

import { onFlow } from "@genkit-ai/firebase/functions";

export const generatePoem = onFlow(
  ai,
  {
    name: "jokeTeller",
    inputSchema: z.string().nullable(),
    outputSchema: z.string(),
    streamSchema: z.string(),
  },
  async (type, streamingCallback) => {
    const { stream, response } = await ai.generateStream(
      `Tell me a longish ${type ?? "dad"} joke.`
    );
    for await (const chunk of stream) {
      streamingCallback(chunk.text);
    }
    return (await response).text;
  }
);

Novo:

import { onCallGenkit } from "firebase-functions/https";
import { defineSecret } from "firebase-functions/params";
import { genkit, z } from "genkit";

const apiKey = defineSecret("GOOGLE_GENAI_API_KEY");

const ai = genkit({
  plugins: [googleAI()],
  model: gemini15Flash,
});

export const jokeTeller = ai.defineFlow(
  {
    name: "jokeTeller",
    inputSchema: z.string().nullable(),
    outputSchema: z.string(),
    streamSchema: z.string(),
  },
  async (type, { sendChunk }) => {
    const { stream, response } = ai.generateStream(
      `Tell me a longish ${type ?? "dad"} joke.`
    );
    for await (const chunk of stream) {
      sendChunk(chunk.text);
    }
    return (await response).text;
  }
);

export const tellJoke = onCallGenkit({ secrets: [apiKey] }, jokeTeller);

As políticas de autenticação foram removidas de defineFlow. O processamento de políticas de autenticação agora depende do servidor.

Antigo:

export const simpleFlow = ai.defineFlow(
  {
    name: 'simpleFlow',
    authPolicy: (auth, input) => {
      // auth policy
    },
  },
  async (input) => {
    // Flow logic here...
  }
);

O snippet a seguir mostra um exemplo de como processar a autenticação no Express.

Novo:

import { UserFacingError } from 'genkit';
import { ContextProvider, RequestData } from 'genkit/context';
import { expressHandler, startFlowServer } from '@genkit-ai/express';

const context: ContextProvider<Context> = (req: RequestData) => {
  return {
    auth: parseAuthToken(req.headers['authorization']),
  };
};

export const simpleFlow = ai.defineFlow(
  {
    name: 'simpleFlow',
  },
  async (input, { context }) => {
    if (!context.auth) {
      throw new UserFacingError("UNAUTHORIZED", "Authorization required.");
    }
    if (input.uid !== context.auth.uid) {
      throw new UserFacingError("UNAUTHORIZED", "You may only summarize your own profile data.");
    }
    // Flow logic here...
  }
);

const app = express();
app.use(express.json());
app.post(
  '/simpleFlow',
  expressHandler(simpleFlow, { context })
);
app.listen(8080);

// or

startFlowServer(
  flows: [withContextProvider(simpleFlow, context)],
  port: 8080
);

Para mais detalhes, consulte a documentação de autenticação.

O snippet a seguir mostra um exemplo de como processar a autenticação no Cloud Functions para Firebase:

import { genkit } from 'genkit';
import { onCallGenkit } from 'firebase-functions/https';

const ai = genkit({ ... });;

const simpleFlow = ai.defineFlow({
  name: 'simpleFlow',
}, async (input) => {
  // Flow logic here...
});

export const selfSummary = onCallGenkit({
  authPolicy: (auth, data) => auth?.token?.['email_verified'] && auth?.token?.['admin'],
}, simpleFlow);

Comandos

Fizemos várias mudanças e melhorias nas instruções.

É possível definir modelos separados para mensagens do sistema e de solicitação:

const hello = ai.definePrompt({
  name: 'hello',
  system: 'talk like a pirate.',
  prompt: 'hello {{ name }}',
  input: {
    schema: z.object({
      name: z.string()
    })
  }
});
const { text } = await hello({name: 'Genkit'});

Também é possível definir solicitações de várias mensagens no campo de mensagens:

const hello = ai.definePrompt({
  name: 'hello',
  messages: '{{ role "system" }} talk like a pirate. {{ role "user" }} hello {{ name }}',
  input: {
    schema: z.object({
      name: z.string()
    })
  }
});

Em vez de modelos de comando, você pode usar uma função:

ai.definePrompt({
  name: 'hello',
  prompt: async (input, { context }) => {
    return `hello ${input.name}`
  },
  input: {
    schema: z.object({
      name: z.string()
    })
  }
});

É possível acessar o contexto (incluindo informações de autenticação) no comando:

const hello = ai.definePrompt({
  name: 'hello',
  messages: 'hello {{ @auth.email }}',
});

As funções de streaming não exigem um await

Antigo:

const { stream, response } = await ai.generateStream(`hi`);
const { stream, output } = await myflow.stream(`hi`);

Novo:

const { stream, response } = ai.generateStream(`hi`);
const { stream, output } = myflow.stream(`hi`);

A incorporação tem um novo tipo de retorno

Adicionamos suporte a embeddings multimodais. Em vez de retornar apenas um vetor de embedding, o método "Embed" retorna uma matriz de objetos de embedding, cada um contendo um vetor e metadados de embedding.

Antigo:

const response = await ai.embed({embedder, content, options});  // returns number[]

Novo:

const response = await ai.embed({embedder, content, options}); // returns Embedding[]
const firstEmbeddingVector = response[0].embedding;  // is number[]