diff --git a/.textlintignore b/.textlintignore index 11c7549360..754516ca52 100644 --- a/.textlintignore +++ b/.textlintignore @@ -1 +1,2 @@ **/*.en.md +tools/**/*.md diff --git a/adev-ja/src/app/sub-navigation-data.ts b/adev-ja/src/app/sub-navigation-data.ts index a4548ff531..d0cac1f8d6 100644 --- a/adev-ja/src/app/sub-navigation-data.ts +++ b/adev-ja/src/app/sub-navigation-data.ts @@ -619,7 +619,7 @@ const DOCS_SUB_NAVIGATION_DATA: NavigationItem[] = [ label: 'Build with AI', children: [ { - label: 'Get Started', + label: 'はじめよう', path: 'ai', contentPath: 'ai/overview', }, diff --git a/adev-ja/src/content/ai/overview.en.md b/adev-ja/src/content/ai/overview.en.md new file mode 100644 index 0000000000..feeda50560 --- /dev/null +++ b/adev-ja/src/content/ai/overview.en.md @@ -0,0 +1,145 @@ + + +Build AI-powered apps. Develop faster with AI. + + +Generative AI (GenAI) with large language models (LLMs) enables the creation of sophisticated and engaging application experiences, including personalized content, intelligent recommendations, media generation and comprehension, information summarization, and dynamic functionality. + +Developing features like these would have previously required deep domain expertise and significant engineering effort. However, new products and SDKs are lowering the barrier to entry. Angular is well-suited for integrating AI into your web application as a result of: + +* Angular's robust templating APIs enable the creation of dynamic, cleanly composed UIs made from generated content +* Strong, signal-based architecture designed to dynamically manage data and state +* Angular integrates seamlessly with AI SDKs and APIs + +This guide demonstrates how you can use [Genkit](/ai#build-ai-powered-applications-with-genkit-and-angular), [Firebase AI Logic](https://firebase.google.com/products/firebase-ai-logic), and the [Gemini API](https://ai.google.dev/) to infuse your Angular apps with AI today. This guide will jumpstart your AI-powered web app development journey by explaining how to begin integrating AI into Angular apps. This guide also shares resources, such as starter kits, example code, and recipes for common workflows, you can use to get up to speed quickly. + +To get started, you should have a basic understanding of Angular. New to Angular? Try our [essentials guide](/essentials) or our [getting started tutorials](/tutorials). + +NOTE: While this page features integrations and examples with Google AI products, tools like Genkit are model agnostic and allow you to choose your own model. In many cases, the examples and code samples are applicable to other third-party solutions. + +## Getting Started +Building AI-powered applications is a new and rapidly developing field. It can be challenging to decide where to start and which technologies to choose. The following section provides three options to choose from: + +1. *Genkit* gives you the choice of [supported model and interface with a unified API](https://firebase.google.com/docs/genkit) for building full-stack applications. Ideal for applications requiring sophisticated back-end AI logic, such as personalized recommendations. + +1. *Firebase AI Logic* provides a secure client-side API for Google's models to build client-side only applications or mobile apps. Best for interactive AI features directly in the browser, such as real-time text analysis or basic chatbots. + +1. *Gemini API* enables you to build an application that uses the methods and functionality exposed through the API surface directly, best for full-stack applications. Suitable for applications needing direct control over AI models, like custom image generation or deep data processing. + +### Build AI-powered applications with Genkit and Angular +[Genkit](https://firebase.google.com/docs/genkit) is an open-source toolkit designed to help you build AI-powered features in web and mobile apps. It offers a unified interface for integrating AI models from Google, OpenAI, Anthropic, Ollama, and more, so you can explore and choose the best models for your needs. As a server-side solution, your web apps need a supported server environment, such as a node-based server in order to integrate with Genkit. Building a full-stack app using Angular SSR gives you the starting server-side code, for example. + +Here are examples of how to build with Genkit and Angular: + +* [Agentic Apps with Genkit and Angular starter-kit](https://github.com/angular/examples/tree/main/genkit-angular-starter-kit)— New to building with AI? Start here with a basic app that features an agentic workflow. Perfect place to start for your first AI building experience. + +* [Use Genkit in an Angular app](https://firebase.google.com/docs/genkit/angular)— Build a basic application that uses Genkit Flows, Angular and Gemini 2.0 Flash. This step-by-step walkthrough guides you through creating a full-stack Angular application with AI features. + +* [Dynamic Story Generator app](https://github.com/angular/examples/tree/main/genkit-angular-story-generator)— Learn to build an agentic Angular app powered by Genkit, Gemini and Imagen 3 to dynamically generate a story based on user interaction featuring beautiful image panels to accompany the events that take place. Start here if you'd like to experiment with a more advanced use-case. + + This example also has an in-depth video walkthrough of the functionality: + * [Watch "Building Agentic Apps with Angular and Genkit live!"](https://youtube.com/live/mx7yZoIa2n4?feature=share) + * [Watch "Building Agentic Apps with Angular and Genkit live! PT 2"](https://youtube.com/live/YR6LN5_o3B0?feature=share) + +* [Building Agentic apps with Firebase and Google Cloud (Barista Example)](https://developers.google.com/solutions/learn/agentic-barista) - Learn how to build an agentic coffee ordering app with Firebase and Google Cloud. This example uses both Firebase AI Logic and Genkit. + +### Build AI-powered applications with Firebase AI Logic and Angular +[Firebase AI Logic](https://firebase.google.com/products/vertex-ai-in-firebase) provides a secure way to interact with Vertex AI Gemini API or Imagen API directly from your web and mobile apps. This is compelling for Angular developers since apps can be either full-stack or client-side only. If you are developing a client-side only application, Firebase AI Logic is a good fit for incorporating AI into your web apps. + +Here is an example of how to build with Firebase AI Logic and Angular: +* [Firebase AI Logic x Angular Starter Kit](https://github.com/angular/examples/tree/main/vertex-ai-firebase-angular-example) - Use this starter-kit to build an e-commerce application with a chat agent that can perform tasks. Start here if you do not have experience building with Firebase AI Logic and Angular. + + This example includes an [in-depth video walkthrough explaining the functionality and demonstrates how to add new features](https://youtube.com/live/4vfDz2al_BI). + +### Build AI-powered applications with Gemini API and Angular +The [Gemini API](https://ai.google.dev/gemini-api/docs) provides access to state-of-the-art models from Google that supports audio, images, video, and text input. The models that are optimized for specific use cases, [learn more on the Gemini API documentation site](https://ai.google.dev/gemini-api/docs/models). + +* [AI Text Editor Angular app template](https://github.com/FirebaseExtended/firebase-framework-tools/tree/main/starters/angular/ai-text-editor) - Use this template to start with a fully functioning text editor with AI-powered features like refining text, expanding text and formalizing text. This is a good starting point to gain experience with calling the Gemini API via HTTP. + +* [AI Chatbot app template](https://github.com/FirebaseExtended/firebase-framework-tools/tree/main/starters/angular/ai-chatbot) - This template starts with a chatbot user interface that communicates with the Gemini API via HTTP. + +## AI patterns in action: Streaming chat responses +Having text appear as the response is received from the model is a common UI pattern for web apps using AI. You can achieve this asynchronous task with Angular's `resource` API. The `stream` property of `resource` accepts an asynchronous function you can use to apply updates to a signal value over time. The signal being updated represents the data being streamed. + +```ts +characters = resource({ + stream: async () => { + const data = signal<{ value: string } | { error: unknown }>({ + value: "", + }); + + fetch(this.url).then(async (response) => { + if (!response.body) return; + + for await (const chunk of response.body) { + const chunkText = this.decoder.decode(chunk); + data.update((prev) => { + if ("value" in prev) { + return { value: `${prev.value} ${chunkText}` }; + } else { + return { error: chunkText }; + } + }); + } + }); + + return data; + }, + }); + +``` + +The `characters` member is updated asynchronously and can be displayed in the template. + +```html +

{{ characters.value() }}

+``` + +On the server side, in `server.ts` for example, the defined endpoint sends the data to be streamed to the client. The following code uses the Gemini API but this technique is applicable to other tools and frameworks that support streaming responses from LLMs: + +```ts + app.get("/api/stream-response", async (req, res) => { + ai.models.generateContentStream({ + model: "gemini-2.0-flash", + contents: "Explain how AI works", + }).then(async (response) => { + for await (const chunk of response) { + res.write(chunk.text); + } + }); + }); + +``` +This example connects to the Gemini API but other APIs that support streaming responses can be used here as well. [You can find the complete example on the Angular Github](https://github.com/angular/examples/tree/main/streaming-example). + +## Best Practices +### Connecting to model providers and keeping your API Credentials Secure +When connecting to model providers, it is important to keep your API secrets safe. *Never put your API key in a file that ships to the client, such as `environments.ts`*. + +Your application's architecture determines which AI APIs and tools to choose. Specifically, choose based on whether or not your application is client-side or server-side. Tools such as Firebase AI Logic provide a secure connection to the model APIs for client-side code. If you want to use a different API than Firerbase AI Logic or prefer to use a different model provider, consider creating a proxy-server or even [Cloud Functions for Firebase](https://firebase.google.com/docs/functions) to serve as a proxy and not expose your API keys. + +For an example of connecting using a client-side app, see the code: [Firebase AI Logic Angular example repository](https://github.com/angular/examples/tree/main/vertex-ai-firebase-angular-example). + +For server-side connections to model APIs that require API keys, prefer using a secrets manager or environment variable, not `environments.ts`. You should follow standard best practices for securing API keys and credentials. Firebase now provides a new secrets manager with the latest updates from Firebase App Hosting. To learn more, [check out the official documentation](https://firebase.google.com/docs/app-hosting/configure). + +For a server-side connection example in a full-stack application, see the code: [Angular AI Example (Genkit and Angular Story Generator) repository](https://github.com/angular/examples/tree/main/genkit-angular-story-generator). + +### Use Tool Calling to enhance apps +If you want to build agentic workflows, where agents are able to act and use tools to solve problems based on prompts use "tool calling". Tool calling, also known as function calling, is a way to provide LLMs the ability to make requests back to the application that called it. As a developer, you define which tools are available and you are in control of how or when the tools are called. + +Tool calling further enhances your web apps by expanding your AI integration further than a question and answer style chat bot. In fact, you can empower your model to request function calls using the function calling API of your model provider. The available tools can be used to perform more complex actions within the context of your application. + +In the [e-commerce example](https://github.com/angular/examples/blob/main/vertex-ai-firebase-angular-example/src/app/ai.service.ts#L88) of the [Angular examples repository](https://github.com/angular/examples), the LLM requests to make calls to functions for inventory in order to gain the necessary context to perform more complex tasks such as calculating how much a group of items in the store will cost. The scope of the available API is up to you as a developer just as is whether or not to call a function requested by the LLM. You remain in control of the flow of execution. You can expose specific functions of a service for example but not all functions of that service. + +### Handling non-deterministic responses +Because models can return non-deterministic results, your applications should be designed with that in mind. Here are a few strategies that you can use in your application implementation: +* Adjust prompts and model parameters (such as [temperature](https://ai.google.dev/gemini-api/docs/prompting-strategies)) for more or less deterministic responses. You can [find out more in the prompting strategies section](https://ai.google.dev/gemini-api/docs/prompting-strategies) of [ai.google.dev](https://ai.google.dev/). +* Use the "human in the loop" strategy where a human verifies outputs before proceeding in a workflow. Build your application workflows to allow operators (humans or other models) to verify outputs and confirm key decisions. +* Employ tool (or function) calling and schema constraints to guide and restrict model responses to predefined formats, increasing response predictability. + +Even considering these strategies and techniques, sensible fallbacks should be incorporated in your application design. Follow existing standards of application resiliency. For example, it is not acceptable for an application to crash if a resource or API is not available. In that scenario, an error message is displayed to the user and, if applicable, options for next steps are also displayed. Building AI-powered applications requires the same consideration. Confirm that the response is aligned with the expected output and provide a "safe landing" in case it is not aligned by way of [graceful degradation](https://developer.mozilla.org/en-US/docs/Glossary/Graceful_degradation). This also applies to API outages for LLM providers. + +Consider this example: The LLM provider is not responding. A potential strategy to handle the outage is: +* Save the response from the user to used in a retry scenario (now or at a later time) +* Alert the user to the outage with an appropriate message that doesn't reveal sensitive information +* Resume the conversation at a later time once the services are available again. diff --git a/adev-ja/src/content/ai/overview.md b/adev-ja/src/content/ai/overview.md index feeda50560..d689d9381f 100644 --- a/adev-ja/src/content/ai/overview.md +++ b/adev-ja/src/content/ai/overview.md @@ -1,65 +1,65 @@ - -Build AI-powered apps. Develop faster with AI. + +AI搭載アプリケーションを構築。AIで開発を加速。 -Generative AI (GenAI) with large language models (LLMs) enables the creation of sophisticated and engaging application experiences, including personalized content, intelligent recommendations, media generation and comprehension, information summarization, and dynamic functionality. +生成AI (GenAI)と大規模言語モデル (LLM)は、パーソナライズされたコンテンツ、インテリジェントなレコメンデーション、メディアの生成と理解、情報の要約、動的な機能など、高度で魅力的なアプリケーション体験の作成を可能にします。 -Developing features like these would have previously required deep domain expertise and significant engineering effort. However, new products and SDKs are lowering the barrier to entry. Angular is well-suited for integrating AI into your web application as a result of: +このような機能の開発は、これまで深いドメイン知識と多大なエンジニアリング作業を必要としました。しかし、新しい製品とSDKが参入障壁を下げています。Angularは、以下の理由により、AIをウェブアプリケーションに統合するのに非常に適しています。 -* Angular's robust templating APIs enable the creation of dynamic, cleanly composed UIs made from generated content -* Strong, signal-based architecture designed to dynamically manage data and state -* Angular integrates seamlessly with AI SDKs and APIs +* Angularの堅牢なテンプレートAPIにより、生成されたコンテンツから動的で整然と構成されたUIを作成できます +* データと状態を動的に管理するために設計された、強力なシグナルベースのアーキテクチャ +* AngularはAI SDKとAPIにシームレスに統合 -This guide demonstrates how you can use [Genkit](/ai#build-ai-powered-applications-with-genkit-and-angular), [Firebase AI Logic](https://firebase.google.com/products/firebase-ai-logic), and the [Gemini API](https://ai.google.dev/) to infuse your Angular apps with AI today. This guide will jumpstart your AI-powered web app development journey by explaining how to begin integrating AI into Angular apps. This guide also shares resources, such as starter kits, example code, and recipes for common workflows, you can use to get up to speed quickly. +このガイドでは、[Genkit](/ai#build-ai-powered-applications-with-genkit-and-angular)、[Firebase AI Logic](https://firebase.google.com/products/firebase-ai-logic)、および[Gemini API](https://ai.google.dev/)を使用して、AngularアプリケーションにAIを組み込む方法を示します。このガイドは、AngularアプリケーションにAIを統合する方法を説明することで、AI搭載ウェブアプリケーション開発の旅を加速させるでしょう。また、このガイドでは、迅速に習得できるスターターキット、サンプルコード、一般的なワークフローのレシピなどのリソースも共有しています。 -To get started, you should have a basic understanding of Angular. New to Angular? Try our [essentials guide](/essentials) or our [getting started tutorials](/tutorials). +始めるには、Angularの基本的な理解が必要です。Angularは初めてですか?[必須ガイド](/essentials)または[入門チュートリアル](/tutorials)をお試しください。 -NOTE: While this page features integrations and examples with Google AI products, tools like Genkit are model agnostic and allow you to choose your own model. In many cases, the examples and code samples are applicable to other third-party solutions. +NOTE: このページではGoogle AI製品との統合と例を紹介していますが、Genkitのようなツールはモデル非依存であり、独自のモデルを選択できます。多くの場合、これらの例とコードサンプルは他のサードパーティソリューションにも適用できます。 -## Getting Started -Building AI-powered applications is a new and rapidly developing field. It can be challenging to decide where to start and which technologies to choose. The following section provides three options to choose from: +## はじめに +AI搭載アプリケーションの構築は、新しく急速に発展している分野です。どこから始め、どの技術を選択するかを決定するのは難しい場合があります。以下のセクションでは、選択できる3つのオプションを提供します。 -1. *Genkit* gives you the choice of [supported model and interface with a unified API](https://firebase.google.com/docs/genkit) for building full-stack applications. Ideal for applications requiring sophisticated back-end AI logic, such as personalized recommendations. +1. *Genkit*は、フルスタックアプリケーション構築のために、[サポートされているモデルと統合APIを備えたインターフェース](https://firebase.google.com/docs/genkit)の選択肢を提供します。パーソナライズされたレコメンデーションなど、高度なバックエンドAIロジックを必要とするアプリケーションに最適です。 -1. *Firebase AI Logic* provides a secure client-side API for Google's models to build client-side only applications or mobile apps. Best for interactive AI features directly in the browser, such as real-time text analysis or basic chatbots. +1. *Firebase AI Logic*は、Googleのモデル向けに安全なクライアントサイドAPIを提供し、クライアントサイド専用アプリケーションやモバイルアプリケーションを構築できます。リアルタイムテキスト分析や基本的なチャットボットなど、ブラウザで直接インタラクティブなAI機能を利用するのに最適です。 -1. *Gemini API* enables you to build an application that uses the methods and functionality exposed through the API surface directly, best for full-stack applications. Suitable for applications needing direct control over AI models, like custom image generation or deep data processing. +1. *Gemini API*を使用すると、APIサーフェスを通じて直接公開されるメソッドと機能を使用するアプリケーションを構築でき、フルスタックアプリケーションに最適です。カスタム画像生成やディープデータ処理など、AIモデルを直接制御する必要があるアプリケーションに適しています。 -### Build AI-powered applications with Genkit and Angular -[Genkit](https://firebase.google.com/docs/genkit) is an open-source toolkit designed to help you build AI-powered features in web and mobile apps. It offers a unified interface for integrating AI models from Google, OpenAI, Anthropic, Ollama, and more, so you can explore and choose the best models for your needs. As a server-side solution, your web apps need a supported server environment, such as a node-based server in order to integrate with Genkit. Building a full-stack app using Angular SSR gives you the starting server-side code, for example. +### GenkitとAngularでAI搭載アプリケーションを構築する {#build-ai-powered-applications-with-genkit-and-angular} +[Genkit](https://firebase.google.com/docs/genkit)は、ウェブアプリケーションやモバイルアプリケーションにAI搭載機能を構築するのに役立つように設計されたオープンソースツールキットです。Google、OpenAI、Anthropic、OllamaなどからのAIモデルを統合するための統合インターフェースを提供するため、ニーズに最適なモデルを探索して選択できます。サーバーサイドソリューションであるため、Genkitと統合するには、ウェブアプリケーションにはNode.jsベースのサーバーなどのサポートされているサーバー環境が必要です。たとえば、Angular SSRを使用してフルスタックアプリケーションを構築すると、サーバーサイドの開始コードが得られます。 -Here are examples of how to build with Genkit and Angular: +GenkitとAngularで構築する方法の例を次に示します。 -* [Agentic Apps with Genkit and Angular starter-kit](https://github.com/angular/examples/tree/main/genkit-angular-starter-kit)— New to building with AI? Start here with a basic app that features an agentic workflow. Perfect place to start for your first AI building experience. +* [GenkitとAngularのスターターキットを使用したエージェントアプリ](https://github.com/angular/examples/tree/main/genkit-angular-starter-kit)— AIでの構築は初めてですか?エージェントワークフローを備えた基本的なアプリケーションから始めましょう。初めてのAI構築体験に最適な場所です。 -* [Use Genkit in an Angular app](https://firebase.google.com/docs/genkit/angular)— Build a basic application that uses Genkit Flows, Angular and Gemini 2.0 Flash. This step-by-step walkthrough guides you through creating a full-stack Angular application with AI features. +* [AngularアプリでGenkitを使用する](https://firebase.google.com/docs/genkit/angular)— Genkit Flows、Angular、Gemini 2.0 Flashを使用する基本的なアプリケーションを構築します。このステップバイステップのウォークスルーは、AI機能を備えたフルスタックAngularアプリケーションの作成をガイドします。 -* [Dynamic Story Generator app](https://github.com/angular/examples/tree/main/genkit-angular-story-generator)— Learn to build an agentic Angular app powered by Genkit, Gemini and Imagen 3 to dynamically generate a story based on user interaction featuring beautiful image panels to accompany the events that take place. Start here if you'd like to experiment with a more advanced use-case. +* [動的ストーリー生成アプリ](https://github.com/angular/examples/tree/main/genkit-angular-story-generator)— Genkit、Gemini、Imagen 3を搭載したエージェントAngularアプリケーションを構築し、ユーザーインタラクションに基づいてストーリーを動的に生成し、発生するイベントに付随する美しい画像パネルを特徴とする方法を学びます。より高度なユースケースを試したい場合は、ここから始めましょう。 - This example also has an in-depth video walkthrough of the functionality: - * [Watch "Building Agentic Apps with Angular and Genkit live!"](https://youtube.com/live/mx7yZoIa2n4?feature=share) - * [Watch "Building Agentic Apps with Angular and Genkit live! PT 2"](https://youtube.com/live/YR6LN5_o3B0?feature=share) + この例には、機能の詳細なビデオウォークスルーも含まれています。 + * [「AngularとGenkitでエージェントアプリを構築するライブ!」を見る](https://youtube.com/live/mx7yZoIa2n4?feature=share) + * [「AngularとGenkitでエージェントアプリを構築するライブ!パート2」を見る](https://youtube.com/live/YR6LN5_o3B0?feature=share) -* [Building Agentic apps with Firebase and Google Cloud (Barista Example)](https://developers.google.com/solutions/learn/agentic-barista) - Learn how to build an agentic coffee ordering app with Firebase and Google Cloud. This example uses both Firebase AI Logic and Genkit. +* [FirebaseとGoogle Cloudでエージェントアプリを構築する(バリスタの例)](https://developers.google.com/solutions/learn/agentic-barista) - FirebaseとGoogle Cloudでエージェントコーヒー注文アプリケーションを構築する方法を学びます。この例では、Firebase AI LogicとGenkitを使用しています。 -### Build AI-powered applications with Firebase AI Logic and Angular -[Firebase AI Logic](https://firebase.google.com/products/vertex-ai-in-firebase) provides a secure way to interact with Vertex AI Gemini API or Imagen API directly from your web and mobile apps. This is compelling for Angular developers since apps can be either full-stack or client-side only. If you are developing a client-side only application, Firebase AI Logic is a good fit for incorporating AI into your web apps. +### Firebase AI LogicとAngularでAI搭載アプリケーションを構築する {#build-ai-powered-applications-with-firebase-ai-logic-and-angular} +[Firebase AI Logic](https://firebase.google.com/products/vertex-ai-in-firebase)は、Vertex AI Gemini APIやImagen APIとウェブアプリケーションやモバイルアプリケーションから直接安全にやり取りする方法を提供します。これは、アプリケーションがフルスタックまたはクライアントサイド専用のいずれかであるため、Angular開発者にとって魅力的です。クライアントサイド専用アプリケーションを開発している場合、Firebase AI LogicはウェブアプリケーションにAIを組み込むのに適しています。 -Here is an example of how to build with Firebase AI Logic and Angular: -* [Firebase AI Logic x Angular Starter Kit](https://github.com/angular/examples/tree/main/vertex-ai-firebase-angular-example) - Use this starter-kit to build an e-commerce application with a chat agent that can perform tasks. Start here if you do not have experience building with Firebase AI Logic and Angular. +Firebase AI LogicとAngularで構築する方法の例を次に示します。 +* [Firebase AI Logic x Angularスターターキット](https://github.com/angular/examples/tree/main/vertex-ai-firebase-angular-example) - このスターターキットを使用して、タスクを実行できるチャットエージェントを備えたeコマースアプリケーションを構築します。Firebase AI LogicとAngularでの構築経験がない場合は、ここから始めましょう。 - This example includes an [in-depth video walkthrough explaining the functionality and demonstrates how to add new features](https://youtube.com/live/4vfDz2al_BI). + この例には、[機能の説明と新機能の追加方法を示す詳細なビデオウォークスルー](https://youtube.com/live/4vfDz2al_BI)が含まれています。 -### Build AI-powered applications with Gemini API and Angular -The [Gemini API](https://ai.google.dev/gemini-api/docs) provides access to state-of-the-art models from Google that supports audio, images, video, and text input. The models that are optimized for specific use cases, [learn more on the Gemini API documentation site](https://ai.google.dev/gemini-api/docs/models). +### Gemini APIとAngularでAI搭載アプリケーションを構築する {#build-ai-powered-applications-with-gemini-api-and-angular} +[Gemini API](https://ai.google.dev/gemini-api/docs)は、音声、画像、動画、テキスト入力をサポートするGoogleの最先端モデルへのアクセスを提供します。特定のユースケースに最適化されたモデルについては、[Gemini APIドキュメントサイトで詳細を確認してください](https://ai.google.dev/gemini-api/docs/models)。 -* [AI Text Editor Angular app template](https://github.com/FirebaseExtended/firebase-framework-tools/tree/main/starters/angular/ai-text-editor) - Use this template to start with a fully functioning text editor with AI-powered features like refining text, expanding text and formalizing text. This is a good starting point to gain experience with calling the Gemini API via HTTP. +* [AIテキストエディターAngularアプリテンプレート](https://github.com/FirebaseExtended/firebase-framework-tools/tree/main/starters/angular/ai-text-editor) - このテンプレートを使用して、テキストの洗練、テキストの拡張、テキストの形式化などのAI搭載機能を備えた完全に機能するテキストエディターから始めましょう。これは、HTTP経由でのGemini API呼び出しの経験を積むのに良い出発点です。 -* [AI Chatbot app template](https://github.com/FirebaseExtended/firebase-framework-tools/tree/main/starters/angular/ai-chatbot) - This template starts with a chatbot user interface that communicates with the Gemini API via HTTP. +* [AIチャットボットアプリテンプレート](https://github.com/FirebaseExtended/firebase-framework-tools/tree/main/starters/angular/ai-chatbot) - このテンプレートは、HTTP経由でGemini APIと通信するチャットボットユーザーインターフェースから始まります。 -## AI patterns in action: Streaming chat responses -Having text appear as the response is received from the model is a common UI pattern for web apps using AI. You can achieve this asynchronous task with Angular's `resource` API. The `stream` property of `resource` accepts an asynchronous function you can use to apply updates to a signal value over time. The signal being updated represents the data being streamed. +## AIパターン実践: チャット応答のストリーミング {#ai-patterns-in-action-streaming-chat-responses} +モデルから応答が受信されるにつれてテキストが表示されるのは、AIを使用するWebアプリケーションで一般的なUIパターンです。この非同期タスクはAngularの`resource` APIで実現できます。`resource`の`stream`プロパティは、時間の経過とともにシグナル値に更新を適用するために使用できる非同期関数を受け入れます。更新されるシグナルは、ストリーミングされるデータを表します。 ```ts characters = resource({ @@ -89,13 +89,13 @@ characters = resource({ ``` -The `characters` member is updated asynchronously and can be displayed in the template. +`characters`メンバーは非同期で更新され、テンプレートに表示できます。 ```html

{{ characters.value() }}

``` -On the server side, in `server.ts` for example, the defined endpoint sends the data to be streamed to the client. The following code uses the Gemini API but this technique is applicable to other tools and frameworks that support streaming responses from LLMs: +サーバー側では、例えば`server.ts`で、定義されたエンドポイントがクライアントにストリーミングされるデータを送信します。以下のコードはGemini APIを使用していますが、この手法はLLMからのストリーミング応答をサポートする他のツールやフレームワークにも適用可能です。 ```ts app.get("/api/stream-response", async (req, res) => { @@ -110,36 +110,36 @@ On the server side, in `server.ts` for example, the defined endpoint sends the d }); ``` -This example connects to the Gemini API but other APIs that support streaming responses can be used here as well. [You can find the complete example on the Angular Github](https://github.com/angular/examples/tree/main/streaming-example). +この例はGemini APIに接続していますが、ストリーミング応答をサポートする他のAPIもここで使用できます。[完全な例はAngularのGithubで見つけることができます](https://github.com/angular/examples/tree/main/streaming-example)。 -## Best Practices -### Connecting to model providers and keeping your API Credentials Secure -When connecting to model providers, it is important to keep your API secrets safe. *Never put your API key in a file that ships to the client, such as `environments.ts`*. +## ベストプラクティス +### モデルプロバイダーへの接続とAPI認証情報の保護 {#connecting-to-model-providers-and-keeping-your-api-credentials-secure} +モデルプロバイダーに接続する際は、APIシークレットを安全に保つことが重要です。*APIキーを`environments.ts`のようなクライアントに配布されるファイルに決して含めないでください*。 -Your application's architecture determines which AI APIs and tools to choose. Specifically, choose based on whether or not your application is client-side or server-side. Tools such as Firebase AI Logic provide a secure connection to the model APIs for client-side code. If you want to use a different API than Firerbase AI Logic or prefer to use a different model provider, consider creating a proxy-server or even [Cloud Functions for Firebase](https://firebase.google.com/docs/functions) to serve as a proxy and not expose your API keys. +アプリケーションのアーキテクチャによって、選択するAI APIとツールが決まります。具体的には、アプリケーションがクライアントサイドかサーバーサイドかに基づいて選択します。Firebase AI Logicのようなツールは、クライアントサイドのコードに対してモデルAPIへの安全な接続を提供します。Firebase AI Logic以外のAPIを使用したい場合や、別のモデルプロバイダーを使用したい場合は、プロキシサーバー、あるいは[Cloud Functions for Firebase](https://firebase.google.com/docs/functions)をプロキシとして使用し、APIキーを公開しないことを検討してください。 -For an example of connecting using a client-side app, see the code: [Firebase AI Logic Angular example repository](https://github.com/angular/examples/tree/main/vertex-ai-firebase-angular-example). +クライアントサイドアプリケーションを使用した接続の例については、コード: [Firebase AI Logic Angular example repository](https://github.com/angular/examples/tree/main/vertex-ai-firebase-angular-example)を参照してください。 -For server-side connections to model APIs that require API keys, prefer using a secrets manager or environment variable, not `environments.ts`. You should follow standard best practices for securing API keys and credentials. Firebase now provides a new secrets manager with the latest updates from Firebase App Hosting. To learn more, [check out the official documentation](https://firebase.google.com/docs/app-hosting/configure). +APIキーを必要とするモデルAPIへのサーバーサイド接続には、`environments.ts`ではなく、シークレットマネージャーまたは環境変数を使用することを推奨します。APIキーと認証情報を保護するための標準的なベストプラクティスに従う必要があります。Firebaseは、Firebase App Hostingの最新アップデートにより、新しいシークレットマネージャーを提供するようになりました。詳細については、[公式ドキュメントを確認してください](https://firebase.google.com/docs/app-hosting/configure)。 -For a server-side connection example in a full-stack application, see the code: [Angular AI Example (Genkit and Angular Story Generator) repository](https://github.com/angular/examples/tree/main/genkit-angular-story-generator). +フルスタックアプリケーションにおけるサーバーサイド接続の例については、コード: [Angular AI Example (Genkit and Angular Story Generator) repository](https://github.com/angular/examples/tree/main/genkit-angular-story-generator)を参照してください。 -### Use Tool Calling to enhance apps -If you want to build agentic workflows, where agents are able to act and use tools to solve problems based on prompts use "tool calling". Tool calling, also known as function calling, is a way to provide LLMs the ability to make requests back to the application that called it. As a developer, you define which tools are available and you are in control of how or when the tools are called. +### ツール呼び出しを使用してアプリケーションを強化する {#use-tool-calling-to-enhance-apps} +エージェントがプロンプトに基づいて問題を解決するために行動し、ツールを使用できるエージェントワークフローを構築したい場合は、「ツール呼び出し」を使用してください。ツール呼び出し(関数呼び出しとも呼ばれる)は、LLMにそれを呼び出したアプリケーションへリクエストを返す機能を提供する方法です。開発者として、どのツールが利用可能かを定義し、ツールがどのように、いつ呼び出されるかを制御します。 -Tool calling further enhances your web apps by expanding your AI integration further than a question and answer style chat bot. In fact, you can empower your model to request function calls using the function calling API of your model provider. The available tools can be used to perform more complex actions within the context of your application. +ツール呼び出しは、AI統合を質疑応答の形式のチャットボットよりもさらに拡張することで、ウェブアプリケーションをさらに強化します。実際、モデルプロバイダーの関数呼び出しAPIを使用して、モデルに関数呼び出しをリクエストさせることができます。利用可能なツールは、アプリケーションのコンテキスト内でより複雑なアクションを実行するために使用できます。 -In the [e-commerce example](https://github.com/angular/examples/blob/main/vertex-ai-firebase-angular-example/src/app/ai.service.ts#L88) of the [Angular examples repository](https://github.com/angular/examples), the LLM requests to make calls to functions for inventory in order to gain the necessary context to perform more complex tasks such as calculating how much a group of items in the store will cost. The scope of the available API is up to you as a developer just as is whether or not to call a function requested by the LLM. You remain in control of the flow of execution. You can expose specific functions of a service for example but not all functions of that service. +[Angular examples repository](https://github.com/angular/examples)の[eコマースの例](https://github.com/angular/examples/blob/main/vertex-ai-firebase-angular-example/src/app/ai.service.ts#L88)では、LLMは、店舗内の商品のグループがいくらになるかを計算するなどのより複雑なタスクを実行するために必要なコンテキストを得るために、在庫に関する関数呼び出しをリクエストします。利用可能なAPIの範囲は、LLMによってリクエストされた関数を呼び出すかどうかも含め、開発者であるあなた次第です。実行フローの制御はあなたが保持します。例えば、サービスの一部の関数は公開できますが、そのサービスのすべての関数を公開する必要はありません。 -### Handling non-deterministic responses -Because models can return non-deterministic results, your applications should be designed with that in mind. Here are a few strategies that you can use in your application implementation: -* Adjust prompts and model parameters (such as [temperature](https://ai.google.dev/gemini-api/docs/prompting-strategies)) for more or less deterministic responses. You can [find out more in the prompting strategies section](https://ai.google.dev/gemini-api/docs/prompting-strategies) of [ai.google.dev](https://ai.google.dev/). -* Use the "human in the loop" strategy where a human verifies outputs before proceeding in a workflow. Build your application workflows to allow operators (humans or other models) to verify outputs and confirm key decisions. -* Employ tool (or function) calling and schema constraints to guide and restrict model responses to predefined formats, increasing response predictability. +### 非決定論的な応答の処理 {#handling-non-deterministic-responses} +モデルは非決定論的な結果を返す可能性があるため、アプリケーションはその点を考慮して設計する必要があります。アプリケーションの実装で利用できるいくつかの戦略を以下に示します。 +* より決定論的な、またはより非決定論的な応答のために、プロンプトとモデルパラメーター([温度](https://ai.google.dev/gemini-api/docs/prompting-strategies)など)を調整します。[ai.google.dev](https://ai.google.dev/)の[プロンプト戦略セクション](https://ai.google.dev/gemini-api/docs/prompting-strategies)で詳細を確認できます。 +* ワークフローを進める前に人間が出力を検証する「ヒューマン・イン・ザ・ループ」戦略を使用します。オペレーター(人間または他のモデル)が出力を検証し、重要な決定を確認できるようにアプリケーションワークフローを構築します。 +* ツール(または関数)呼び出しとスキーマ制約を利用して、モデルの応答を事前定義された形式に誘導および制限し、応答の予測可能性を高めます。 -Even considering these strategies and techniques, sensible fallbacks should be incorporated in your application design. Follow existing standards of application resiliency. For example, it is not acceptable for an application to crash if a resource or API is not available. In that scenario, an error message is displayed to the user and, if applicable, options for next steps are also displayed. Building AI-powered applications requires the same consideration. Confirm that the response is aligned with the expected output and provide a "safe landing" in case it is not aligned by way of [graceful degradation](https://developer.mozilla.org/en-US/docs/Glossary/Graceful_degradation). This also applies to API outages for LLM providers. +これらの戦略や技術を考慮しても、アプリケーション設計には適切なフォールバックを組み込む必要があります。アプリケーションの回復性に関する既存の標準に従ってください。例えば、リソースやAPIが利用できない場合にアプリケーションがクラッシュすることは許容されません。そのシナリオでは、エラーメッセージがユーザーに表示され、該当する場合は次のステップのオプションも表示されます。AIを活用したアプリケーションを構築する場合も、同様の考慮が必要です。応答が期待される出力と一致していることを確認し、一致しない場合は[グレースフルデグラデーション](https://developer.mozilla.org/en-US/docs/Glossary/Graceful_degradation)によって「安全な着地」を提供します。これは、LLMプロバイダーのAPI停止にも適用されます。 -Consider this example: The LLM provider is not responding. A potential strategy to handle the outage is: -* Save the response from the user to used in a retry scenario (now or at a later time) -* Alert the user to the outage with an appropriate message that doesn't reveal sensitive information -* Resume the conversation at a later time once the services are available again. +この例を考えてみましょう: LLMプロバイダーが応答していません。停止を処理するための潜在的な戦略は次のとおりです。 +* ユーザーからの応答を、再試行シナリオ(今すぐまたは後で)で使用するために保存します。 +* 機密情報を開示しない適切なメッセージで、ユーザーに停止を警告します。 +* サービスが再度利用できるようになったら、後で会話を再開します。 diff --git a/package.json b/package.json index 20950214d8..0953b3261c 100644 --- a/package.json +++ b/package.json @@ -17,7 +17,8 @@ }, "packageManager": "yarn@1.22.10", "devDependencies": { - "@google/genai": "^0.8.0", + "@langchain/core": "0.3.58", + "@langchain/google-genai": "0.2.12", "@types/cli-progress": "^3.11.6", "@types/node": "20.14.10", "chokidar": "3.6.0", @@ -25,6 +26,7 @@ "consola": "3.2.3", "execa": "^9.3.0", "globby": "14.0.2", + "langchain": "0.3.28", "rxjs": "7.8.1", "sitemap": "8.0.0", "textlint": "^14.0.4", diff --git a/tools/translator/README.md b/tools/translator/README.md new file mode 100644 index 0000000000..8683545d13 --- /dev/null +++ b/tools/translator/README.md @@ -0,0 +1,108 @@ +# Translator Tool + +MarkdownファイルをGoogle Gemini APIを使用して日本語に翻訳するCLIツール + +## 概要 + +このツールは、Angularドキュメントの英語版Markdownファイルを高品質な日本語に自動翻訳するために開発されました。AI翻訳と自動校正を組み合わせることで、一貫性のある翻訳品質を実現します。 + +### 主な機能 + +- **AI翻訳**: Google Gemini APIを使用した高精度な英日翻訳 +- **自動校正**: textlintによる日本語文章の品質チェックと修正 +- **ブロック分割**: 大きなMarkdownファイルを見出し単位で分割処理 +- **レート制限**: API制限に配慮した段階的処理 +- **ファイル管理**: 原文ファイル(.en.md)と翻訳ファイル(.md)の自動管理 + +## 使用方法 + +### 前提条件 + +1. Google AI Studio でAPIキーを取得 + - https://aistudio.google.com/app/apikey + +2. 環境変数を設定 +```bash +export GOOGLE_API_KEY="your-api-key" +export GEMINI_MODEL="gemini-2.5-flash-preview-05-20" # オプション +``` + +### コマンド実行 + +```bash +# 基本的な使用方法 +yarn translate path/to/file.md + +# 確認なしで保存 +yarn translate -w path/to/file.md + +# ヘルプ表示 +yarn translate --help +``` + +### オプション + +- `-w, --write`: 確認プロンプトなしで翻訳結果を自動保存 +- `-h, --help`: 使用方法を表示 + +## ファイル構成 + +``` +tools/translator/ +├── README.md # このファイル +├── main.ts # CLIエントリーポイント +├── translate.ts # 翻訳処理の主要ロジック +├── agent.ts # LangChainベースの翻訳エージェント +└── utils.ts # Markdown処理ユーティリティ +``` + +## 内部実装の設計 + +### アーキテクチャ + +```mermaid +graph TD + A[main.ts
CLI制御] --> B[translate.ts
翻訳管理] + B --> C[agent.ts
AI翻訳エージェント] + B --> D[utils.ts
Markdown処理] + + style A fill:#e1f5fe + style B fill:#f3e5f5 + style C fill:#e8f5e8 + style D fill:#fff3e0 +``` + +### コンポーネント詳細 + +#### main.ts +- **責任**: CLI制御とワークフロー管理 +- **設計パターン**: 単一責任の原則に基づく関数分離 +- **主要機能**: + - 環境変数・引数の検証 + - ファイル存在確認 + - ヘルプ表示 + - 保存確認プロンプト + +#### translate.ts +- **責任**: 翻訳処理の統合管理 +- **設計パターン**: ファサードパターン +- **主要機能**: + - Markdownファイルの分割処理 + - 進捗表示 + - レート制限制御 + - 翻訳結果の統合 + +#### agent.ts +- **責任**: AI翻訳の実行 +- **設計パターン**: LangChainのRunnableチェーン +- **主要機能**: + - 翻訳プロンプトの管理 + - 翻訳 → 校正の2段階処理 + - textlintによる品質チェック + +#### utils.ts +- **責任**: Markdown処理ユーティリティ +- **設計パターン**: Pure Functions +- **主要機能**: + - 見出し単位でのMarkdown分割 + - 翻訳ブロックの再統合 diff --git a/tools/translator/agent.ts b/tools/translator/agent.ts new file mode 100644 index 0000000000..89d6c3c6ce --- /dev/null +++ b/tools/translator/agent.ts @@ -0,0 +1,151 @@ +import { StringOutputParser } from '@langchain/core/output_parsers'; +import { + Runnable, + RunnableBranch, + RunnableLambda, + RunnableSequence, +} from '@langchain/core/runnables'; +import { PromptTemplate } from '@langchain/core/prompts'; +import { ChatGoogleGenerativeAI } from '@langchain/google-genai'; +import { createLinter, loadLinterFormatter, loadTextlintrc } from 'textlint'; + +const defaultGeminiModel = 'gemini-2.5-flash-preview-05-20'; + +export type TranslationAgentInput = { + text: string; +}; + +export async function createTranslationAgent(input: { + googleApiKey: string; + translationModelName?: string; + proofreaderModelName?: string; +}): Promise> { + const { googleApiKey, translationModelName, proofreaderModelName } = input; + + // 翻訳用モデル + const translator = new ChatGoogleGenerativeAI({ + apiKey: googleApiKey, + model: translationModelName ?? defaultGeminiModel, + temperature: 0.5, // 翻訳の一貫性を重視 + }); + const translatorPrompt = PromptTemplate.fromTemplate( + translatorPromptTemplate + ); + + // 校正用モデル + const proofreader = new ChatGoogleGenerativeAI({ + apiKey: googleApiKey, + model: proofreaderModelName ?? defaultGeminiModel, + temperature: 0.8, // エラー修正への柔軟性を持たせる + }); + const proofreaderPrompt = PromptTemplate.fromTemplate( + proofreaderPromptTemplate + ); + + const textlint = await createTextlintRunnable(); + + return RunnableSequence.from([ + // { text: string } -> PromptTemplate<'text'> -> AIMessageChunk -> string + translatorPrompt.pipe(translator).pipe(new StringOutputParser()), + // string -> { fixedText: string, diagnostics: string } + textlint, + RunnableBranch.from([ + // Textlintがエラーを検出した場合は校正を行う + [ + (result) => !!result.diagnostics, + // { fixedText: string, diagnostics: string } -> PromptTemplate<'text' | 'diagnostics'> -> AIMessageChunk -> string + proofreaderPrompt.pipe(proofreader).pipe(new StringOutputParser()), + ], + // エラーがない場合はそのままテキストを返す + // { fixedText: string, diagnostics: string } -> string + RunnableLambda.from((result) => result.text), + ]), + ]); +} + +type TextlintRunnableOutput = { text: string; diagnostics: string | null }; + +export async function createTextlintRunnable(): Promise< + Runnable +> { + const descriptor = await loadTextlintrc(); + const linter = await createLinter({ descriptor }); + const linterFormatter = await loadLinterFormatter({ formatterName: 'unix' }); + + return RunnableLambda.from(async (text: string) => { + // 1. 自動修正可能なエラーを修正する + const { output: fixedText } = await linter.fixText(text, 'temp.md'); + // 2. 修正後のテキストを再度lintして診断結果を取得す + const result = await linter.lintText(fixedText, 'temp.md'); + // 3. 診断結果を整形する + if (result.messages.length === 0) { + return { text: fixedText, diagnostics: null }; + } + return { text: fixedText, diagnostics: linterFormatter.format([result]) }; + }); +} + +const translatorPromptTemplate = + `あなたは技術文書の翻訳専門家です。以下のマークダウンテキストを日本語に翻訳してください。 + +## 重要な注意事項 + +- **マークダウンの構造を絶対に変更しないでください** +- **行数を絶対に変更しないでください** - 入力と出力の行数は必ず同じにしてください +- **コードブロック内の内容は翻訳しないでください** +- **URL、ファイル名、識別子は翻訳しないでください** +- **HTML タグや特殊な記号は保持してください** +- **リストの階層構造とマーカー(\\*, -, +, 1.など)を維持してください** +- **見出しレベル(#の数)は変更しないでください** +- **空行は空行のまま保持してください** +- **インデントやスペースを保持してください** +- **技術用語は適切な日本語に翻訳してください** +- **特別なプレフィックスは絶対に変更しないでください** + - 例: NOTE/TIP/HELPFUL/IMPORTANT/QUESTION/TLDR/CRITICAL + - OK: "NOTE: これは重要な情報です。" + - NG: "注: これは重要な情報です。" +- "

" より下位の見出しは、**原文の見出しをlower-caseにしたアンカーIDを付与してください** + - 例: "# Security" → "# セキュリティ" ("

" の場合はアンカーIDなし) + - 例: "## How to use Angular" → "## Angularの使い方 {{#how-to-use-angular}}" + - 例: "### How to use Angular" → "### Angularの使い方 {{#how-to-use-angular}}" +- **英単語の前後にスペースを入れないでください** + - bad: "Angular の使い方" + - good: "Angularの使い方" + +翻訳されたテキストのみを返してください。他の説明や追加のテキストは含めないでください。テキスト全体をコードブロックとしてラップしないでください。 + +## 翻訳対象テキスト + +=== +{text} +=== +` as const; + +const proofreaderPromptTemplate = + `あなたは日本語で書かれた文書の校正専門家です。以下のマークダウンテキストに校正エラーが検出されました。エラーで指摘された部分を修正したテキストを返してください。 + +## 重要な注意事項 + +1. **マークダウンの構造を絶対に変更しないでください** +2. **行数を絶対に変更しないでください** - 入力と出力の行数は必ず同じにしてください +3. **コードブロック内の内容は変更しないでください** +4. **URL、ファイル名、識別子は変更しないでください** +5. **HTML タグや特殊な記号は保持してください** +6. **リストの階層構造とマーカー(*, -, +, 1.など)を維持してください** +7. **見出しレベル(#の数)を変更しないでください** +8. **空行は空行のまま保持してください** +9. **インデントやスペースを保持してください** +10. **指摘されたエラーのみを修正してください** + +修正されたテキストのみを返してください。他の説明や追加のテキストは含めないでください。テキスト全体をコードブロックとしてラップしないでください。 + +## 校正エラー + +{diagnostics} + +## 修正対象テキスト (\`temp.md\`) + +=== +{text} +=== +` as const; diff --git a/tools/translator/main.ts b/tools/translator/main.ts index c7ec62075c..9b006dbcf1 100644 --- a/tools/translator/main.ts +++ b/tools/translator/main.ts @@ -1,7 +1,6 @@ import consola from 'consola'; import assert from 'node:assert'; import { readFile, writeFile } from 'node:fs/promises'; -import { resolve } from 'node:path'; import { parseArgs } from 'node:util'; import { cpRf, @@ -9,64 +8,167 @@ import { getEnFilePath, getLocalizedFilePath, } from '../lib/fsutils'; -import { rootDir } from '../lib/workspace'; -import { GeminiTranslator } from './translate'; +import { MarkdownTranslator } from './translate'; -async function main() { - const apiKey = process.env.GOOGLE_API_KEY; - assert(apiKey, 'GOOGLE_API_KEY 環境変数が設定されていません。'); +/** + * CLI引数の型定義 + */ +interface CliArgs { + file: string; + write?: boolean; + help?: boolean; +} + +/** + * 翻訳に必要な環境変数の検証 + */ +function validateEnvironment(): { googleApiKey: string; geminiModel?: string } { + const googleApiKey = process.env.GOOGLE_API_KEY; + const geminiModel = process.env.GEMINI_MODEL; + + assert(googleApiKey, 'GOOGLE_API_KEY 環境変数が設定されていません。'); + + return { googleApiKey, geminiModel }; +} +/** + * ヘルプメッセージの表示 + * ユーザーにCLIの使用方法を提供するため + */ +function showHelp(): void { + console.log(` +使用方法: npx tsx tools/translator/main.ts [オプション] <ファイルパス> + +Markdownファイルを日本語に翻訳します。 + +オプション: + -w, --write 確認なしで翻訳結果を保存 + -h, --help このヘルプメッセージを表示 + +引数: + <ファイルパス> 翻訳するMarkdownファイルのパス + +環境変数: + GOOGLE_API_KEY Google AI API キー(必須) + GEMINI_MODEL 使用するGeminiモデル(オプション) + +例: + npx tsx tools/translator/main.ts example.md + npx tsx tools/translator/main.ts -w example.md +`); +} + +/** + * コマンドライン引数の解析と検証 + */ +function parseCliArgs(): CliArgs { const args = parseArgs({ - options: { write: { type: 'boolean', default: false, short: 'w' } }, + options: { + write: { type: 'boolean', default: false, short: 'w' }, + help: { type: 'boolean', default: false, short: 'h' }, + }, allowPositionals: true, }); - const { write } = args.values; + + const { write, help } = args.values; const [file] = args.positionals; + if (help) { + showHelp(); + process.exit(0); + } + + if (!file) { + showHelp(); + throw new Error('ファイルパスを指定してください。'); + } + + return { write, file, help }; +} + +/** + * ファイルの存在確認 + */ +async function validateFileExistence(file: string): Promise { const fileExists = await exists(file); if (!fileExists) { throw new Error(`ファイルが見つかりません: ${file}`); } +} +/** + * ファイルの翻訳処理実行 + */ +async function translateFile( + file: string, + googleApiKey: string, + geminiModel?: string +): Promise { + const translator = new MarkdownTranslator(googleApiKey, geminiModel); const content = await readFile(file, 'utf-8'); - const prh = await readFile(resolve(rootDir, 'prh.yml'), 'utf-8'); - const model = process.env.GEMINI_MODEL || 'gemini-2.0-flash'; + return translator.translate(content); +} - const translator = new GeminiTranslator(apiKey, model); - const translated = await translator.translate(file, content, prh); +/** + * 原文ファイルの自動生成 + */ +async function ensureEnglishFile(originalFile: string): Promise { + const enFile = getEnFilePath(originalFile); + if (!(await exists(enFile))) { + consola.warn( + `原文ファイルが見つかりません。入力ファイルを ${enFile} にコピーします。` + ); + await cpRf(originalFile, enFile); + } +} - console.log(translated); - await writeTranslatedContent(file, translated, write); +/** + * 保存確認のユーザープロンプト + */ +async function promptForSave(outputFile: string): Promise { + return consola.prompt(`翻訳結果を保存しますか?\n保存先: ${outputFile}`, { + type: 'confirm', + initial: false, + }); } -async function writeTranslatedContent( +/** + * 翻訳結果の保存処理 + */ +async function saveTranslation( file: string, content: string, - forceWrite = false -) { + forceWrite: boolean +): Promise { const outputFile = getLocalizedFilePath(file); - const save = - forceWrite || - (await consola.prompt(`翻訳結果を保存しますか?\n保存先: ${outputFile}`, { - type: 'confirm', - initial: false, - })); - if (!save) { + const shouldSave = forceWrite || (await promptForSave(outputFile)); + + if (!shouldSave) { return; } - const enFile = getEnFilePath(file); - // .en.* が存在しない場合は原文コピーを忘れているため、.en.md に元ファイルをコピーする - if (!(await exists(enFile))) { - consola.warn( - `原文ファイルが見つかりません。入力ファイルを ${enFile} にコピーします。` - ); - await cpRf(file, enFile); - } + await ensureEnglishFile(file); await writeFile(outputFile, content); consola.success(`保存しました`); } +/** + * アプリケーションのメインエントリーポイント + */ +async function main() { + const { write, file } = parseCliArgs(); + const { googleApiKey, geminiModel } = validateEnvironment(); + + await validateFileExistence(file); + + consola.start(`Starting translation for ${file}`); + + const translated = await translateFile(file, googleApiKey, geminiModel); + + console.log(translated); + await saveTranslation(file, translated, !!write); +} + main().catch((error) => { consola.error(error); process.exit(1); diff --git a/tools/translator/translate.ts b/tools/translator/translate.ts index a0e55b2908..42621b8b46 100644 --- a/tools/translator/translate.ts +++ b/tools/translator/translate.ts @@ -6,201 +6,43 @@ * 発行した API キーは環境変数 GOOGLE_API_KEY に設定してください。 */ -import { GoogleGenAI } from '@google/genai'; import { SingleBar } from 'cli-progress'; -import { consola } from 'consola'; import { setTimeout } from 'node:timers/promises'; -import { - ContentType, - getContentType, - renderContent, - splitMarkdown, - splitRecommendations, -} from './utils'; - -export class GeminiTranslator { - readonly #client: GoogleGenAI; - readonly #model: string; - - constructor(apiKey: string, model: string) { - this.#client = new GoogleGenAI({ apiKey }); - this.#model = model; - console.log(`Using model: ${model}`); - } - - async translate( - filename: string, - content: string, - prh: string - ): Promise { - const contentType = getContentType(filename); - const systemInstruction = getSystemInstruction(contentType, prh); - - const chat = this.#client.chats.create({ - model: this.#model, - config: { systemInstruction, temperature: 0.1 }, +import { renderMarkdown, splitMarkdown } from './utils'; +import { createTranslationAgent } from './agent'; + +export class MarkdownTranslator { + constructor( + private readonly googleApiKey: string, + private readonly geminiModel: string | undefined + ) {} + + async translate(content: string): Promise { + const agent = await createTranslationAgent({ + googleApiKey: this.googleApiKey, + translationModelName: this.geminiModel, + proofreaderModelName: this.geminiModel, }); - consola.start(`Starting translation for ${filename}`); - await chat - .sendMessage({ - message: [ - `これから ${filename} の翻訳作業を開始します。次のメッセージからテキスト断片を入力するので、日本語に翻訳して出力してください。今回の翻訳タスクと遵守するルールをおさらいしてください。`, - ], - }) - .then((response) => { - if (response.text) { - consola.info(`Gemini: ${response.text}`); - } - }); - const progress = new SingleBar({}); + const chunks = splitMarkdown(content); - const blocks = - contentType === 'markdown' - ? splitMarkdown(content) - : splitRecommendations(content); - - progress.start(blocks.length, 0); + progress.start(chunks.length, 0); const rpm = 10; // Requests per minute const waitTime = Math.floor((60 * 1000) / rpm); - const translated = []; - for (const block of blocks) { - const prompt = block.trim(); + const translatedChunks = []; + for (const chunk of chunks) { const delay = setTimeout(waitTime); - const response = await chat.sendMessage({ message: [prompt] }); - translated.push(response.text ?? ''); // Fallback in case of no response + + const translated = await agent.invoke({ text: chunk.trim() }); + translatedChunks.push(translated); progress.increment(); - await delay; // Avoid rate limiting + await delay; } progress.stop(); - return renderContent(contentType, translated); + return renderMarkdown(translatedChunks); } } - -function getSystemInstruction(contentType: ContentType, prh: string): string { - return ` -あなたはオープンソースライブラリの開発者向けドキュメントの翻訳者です。 -入力として与えられたテキストに含まれる英語を日本語に翻訳します。 - -## Task - -ユーザーはテキスト全体を分割し、断片ごとに翻訳を依頼します。 -あなたは与えられた断片を日本語に翻訳し、翻訳結果だけを出力します。 -前回までの翻訳結果を参照しながら、テキスト全体での表現の一貫性を保つようにしてください。 - -${(contentType === 'markdown' - ? ` -## Rules -翻訳は次のルールに従います。 - -- Markdownの構造の変更は禁止されています。 - - 見出しレベル("#")の数を必ず維持する。 - - 例: "# Security" → "# セキュリティ" - - 改行やインデントの数を必ず維持する。 -- トップレベル("

")以外の見出しに限り、元の見出しをlower caseでハイフン結合したアンカーIDとして使用する - - 例: "# Security" → "# セキュリティ" - - 例: "## How to use Angular" → "## Angularの使い方 {#how-to-use-angular}" -- 英単語の前後にスペースを入れない。 - - bad: "Angular の使い方" - - good: "Angularの使い方" -- 特別なプレフィックスは翻訳せずにそのまま残す。 - - 例: NOTE/TIP/HELPFUL/IMPORTANT/QUESTION/TLDR/CRITICAL -- 表現の一貫性を保つため、同じ単語には同じ訳語を使う。 -- 冗長な表現を避け、自然な日本語にする。 - - 例: 「することができます」→「できます」 - -入力例: - ---- -# Security - -This topic describes Angular's built-in protections against common web application vulnerabilities and attacks such as cross-site scripting attacks. -It doesn't cover application-level security, such as authentication and authorization. ---- - -出力例: - ---- -# セキュリティ - -このトピックでは、クロスサイトスクリプティング攻撃などの一般的なWebアプリケーションの脆弱性や攻撃に対する、Angularの組み込みの保護について説明します。 -認証や認可など、アプリケーションレベルのセキュリティは扱いません。 ---- -` - : contentType === 'recommendations' - ? ` -recommendations.tsは次のような形式のオブジェクトを含むTypeScriptファイルです。 - ---- -export const RECOMMENDATIONS: Step[] = [ - { - possibleIn: 200, - necessaryAsOf: 400, - level: ApplicationComplexity.Basic, - step: 'Extends OnInit', - action: - "Ensure you don't use \`extends OnInit\`, or use \`extends\` with any lifecycle event. Instead use \`implements .\`", - }, - { - possibleIn: 200, - necessaryAsOf: 400, - level: ApplicationComplexity.Advanced, - step: 'Deep Imports', - action: - 'Stop using deep imports, these symbols are now marked with ɵ and are not part of our public API.', - }, ---- - -## Rules -翻訳は次のルールに従います。 -- **翻訳対象となるのは "action" フィールドの文字列リテラルのみです。** -- 原文に存在しない行を追加することは禁止されています。 -- 原文に存在する行を削除することは禁止されています。 -- ソースコードの構造の変更は禁止されています。 -- コードのロジックや構造の変更は禁止されています。 -- ソースコードのキーワードや構文の翻訳は禁止されています。 -- 変数名や関数名の翻訳は禁止されています。 - -入力例: ---- -export const RECOMMENDATIONS: Step[] = [ - { - possibleIn: 200, - necessaryAsOf: 400, - level: ApplicationComplexity.Basic, - step: 'Extends OnInit', - action: - "Ensure you don't use \`extends OnInit\`, or use \`extends\` with any lifecycle event. Instead use \`implements .\`", - }, ---- - -出力例: ---- -export const RECOMMENDATIONS: Step[] = [ - { - possibleIn: 200, - necessaryAsOf: 400, - level: ApplicationComplexity.Basic, - step: 'Extends OnInit', - action: - '\`OnInit\`を継承しない、あるいはライフサイクルイベントを使用する場合は\`implements \`を使用してください。', - }, ---- - -` - : `` -).trim()}; - -## 翻訳後の校正 - -表記揺れや不自然な日本語を避けるため、YAML形式で定義されているPRH(proofreading helper)ルールを使用して、翻訳後のテキストを校正します。 -次のPRHルールを使用してください。 ---- -${prh} ---- -`.trim(); -} diff --git a/tools/translator/utils.ts b/tools/translator/utils.ts index 60f6c7d259..37c70c7cca 100644 --- a/tools/translator/utils.ts +++ b/tools/translator/utils.ts @@ -1,61 +1,14 @@ export type ContentBlock = string; -export type ContentType = 'markdown' | 'recommendations'; export function splitMarkdown(content: string): ContentBlock[] { // split content by heading lines (lines starting with ##) return content.split(/\n(?=##\s)/); } -export function splitRecommendations(content: string): ContentBlock[] { - // split content by 300 lines - const size = 300; - if (content.length <= size) { - return [content]; - } - const lines = content.split('\n'); - const blocks: ContentBlock[] = []; - for (let i = 0; i < lines.length; i += size) { - blocks.push(lines.slice(i, i + size).join('\n')); - } - return blocks; -} - -export async function renderContent( - contentType: ContentType, - blocks: ContentBlock[] -) { +export async function renderMarkdown(blocks: ContentBlock[]) { const content = blocks - .map((block) => { - if (contentType === 'markdown') { - // For markdown, ensure each block ends with a newline - return block; - } else { - // For code, ensure no trailing newline - return stripMarkdownBackticks(block); - } - }) .map((block) => (block.endsWith('\n') ? block.replace(/\n$/, '') : block)) .join('\n\n'); // add trailing newline return content + '\n'; } - -export function getContentType(filename: string): ContentType { - // Determine content type based on file extension - if (filename.endsWith('.md')) { - return 'markdown'; - } else if (filename.includes('recommendations')) { - return 'recommendations'; - } - // Default to markdown for other file types - return 'markdown'; -} - -export function stripMarkdownBackticks(content: string): string { - // Trim leading and trailing backticks - // and remove any language specifier - // e.g. ```js or ```typescript - return content - .replace(/^\s*```[a-zA-Z0-9-]*\s*/, '') // leading backticks with optional language - .replace(/\s*```[a-zA-Z0-9-]*\s*$/, ''); // trailing backticks with optional language -} diff --git a/yarn.lock b/yarn.lock index e9608be2df..894839b995 100644 --- a/yarn.lock +++ b/yarn.lock @@ -33,6 +33,11 @@ resolved "https://registry.npmjs.org/@babel/parser/-/parser-7.24.8.tgz" integrity sha512-WzfbgXOkGzZiXXCqk43kKwZjzwx4oulxZi3nq2TYL9mOjQv6kYwul9mz6ID36njuL7Xkp6nJEfok848Zj10j/w== +"@cfworker/json-schema@^4.0.2": + version "4.1.1" + resolved "https://registry.yarnpkg.com/@cfworker/json-schema/-/json-schema-4.1.1.tgz#4a2a3947ee9fa7b7c24be981422831b8674c3be6" + integrity sha512-gAmrUZSGtKc3AiBL71iNWxDsyUC5uMaKKGdvzYsBoTW/xi42JQHl7eKV2OYzCUqvc+D2RCcf7EXY2iCyFIk6og== + "@esbuild/aix-ppc64@0.23.1": version "0.23.1" resolved "https://registry.yarnpkg.com/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz#51299374de171dbd80bb7d838e1cfce9af36f353" @@ -153,13 +158,52 @@ resolved "https://registry.yarnpkg.com/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz#81fd50d11e2c32b2d6241470e3185b70c7b30699" integrity sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg== -"@google/genai@^0.8.0": - version "0.8.0" - resolved "https://registry.yarnpkg.com/@google/genai/-/genai-0.8.0.tgz#caf753288fe0123ab5cfca1f5f8613cc840e0029" - integrity sha512-Zs+OGyZKyMbFofGJTR9/jTQSv8kITh735N3tEuIZj4VlMQXTC0soCFahysJ9NaeenRlD7xGb6fyqmX+FwrpU6Q== +"@google/generative-ai@^0.24.0": + version "0.24.1" + resolved "https://registry.yarnpkg.com/@google/generative-ai/-/generative-ai-0.24.1.tgz#634a3c06f8ea7a6125c1b0d6c1e66bb11afb52c9" + integrity sha512-MqO+MLfM6kjxcKoy0p1wRzG3b4ZZXtPI+z2IE26UogS2Cm/XHO+7gGRBh6gcJsOiIVoH93UwKvW4HdgiOZCy9Q== + +"@langchain/core@0.3.58": + version "0.3.58" + resolved "https://registry.yarnpkg.com/@langchain/core/-/core-0.3.58.tgz#8de745fa1345dfea246adebe42f0cffc41fa9e6f" + integrity sha512-HLkOtVofgBHefaUae/+2fLNkpMLzEjHSavTmUF0YC7bDa5NPIZGlP80CGrSFXAeJ+WCPd8rIK8K/p6AW94inUQ== + dependencies: + "@cfworker/json-schema" "^4.0.2" + ansi-styles "^5.0.0" + camelcase "6" + decamelize "1.2.0" + js-tiktoken "^1.0.12" + langsmith "^0.3.29" + mustache "^4.2.0" + p-queue "^6.6.2" + p-retry "4" + uuid "^10.0.0" + zod "^3.25.32" + zod-to-json-schema "^3.22.3" + +"@langchain/google-genai@0.2.12": + version "0.2.12" + resolved "https://registry.yarnpkg.com/@langchain/google-genai/-/google-genai-0.2.12.tgz#dc9e8a8dae111e9097fb6354ae033a1d69caaecf" + integrity sha512-dVfkNW3uJ2Ing4KAYPTfkdTDIA4FDrik/YK5A5bE8w7drP0J+7g0h2i8D5Mn7dDcyPuX74qd2VG9hWCyGLtZiw== + dependencies: + "@google/generative-ai" "^0.24.0" + uuid "^11.1.0" + +"@langchain/openai@>=0.1.0 <0.6.0": + version "0.5.13" + resolved "https://registry.yarnpkg.com/@langchain/openai/-/openai-0.5.13.tgz#fc8d3d9cbe502ad7a77ff2718ed222ec65eda24c" + integrity sha512-t5UsO7XYE+DBQlXQ21QK74Y+LH4It20wnENrmueNvxIWTn0nHDIGVmO6wo4rJxbmOOPRQ4l/oAxGRnYU8B8v6w== + dependencies: + js-tiktoken "^1.0.12" + openai "^4.96.0" + zod "3.25.32" + +"@langchain/textsplitters@>=0.0.0 <0.2.0": + version "0.1.0" + resolved "https://registry.yarnpkg.com/@langchain/textsplitters/-/textsplitters-0.1.0.tgz#f37620992192df09ecda3dfbd545b36a6bcbae46" + integrity sha512-djI4uw9rlkAb5iMhtLED+xJebDdAG935AdP4eRTB02R7OB/act55Bj9wsskhZsvuyQRpO4O1wQOp85s6T6GWmw== dependencies: - google-auth-library "^9.14.2" - ws "^8.18.0" + js-tiktoken "^1.0.12" "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -510,6 +554,14 @@ dependencies: "@types/unist" "*" +"@types/node-fetch@^2.6.4": + version "2.6.12" + resolved "https://registry.yarnpkg.com/@types/node-fetch/-/node-fetch-2.6.12.tgz#8ab5c3ef8330f13100a7479e2cd56d3386830a03" + integrity sha512-8nneRWKCg3rMtF69nLQJnOYUcbafYeFSjqkw3jCRLsqkWFlHaoQrr5mXmofFGOx3DKn7UfmBMyov8ySvLRVldA== + dependencies: + "@types/node" "*" + form-data "^4.0.0" + "@types/node@*": version "22.0.0" resolved "https://registry.npmjs.org/@types/node/-/node-22.0.0.tgz" @@ -529,6 +581,18 @@ resolved "https://registry.npmjs.org/@types/node/-/node-17.0.45.tgz" integrity sha512-w+tIMs3rq2afQdsPJlODhoUEKzFP1ayaoyl1CcnwtIlsVe7K7bA1NGm4s3PraqTLlXnbIN84zuBlxBWo1u9BLw== +"@types/node@^18.11.18": + version "18.19.112" + resolved "https://registry.yarnpkg.com/@types/node/-/node-18.19.112.tgz#cd2aee9c075402e0e1942a44101428881dbeb110" + integrity sha512-i+Vukt9POdS/MBI7YrrkkI5fMfwFtOjphSmt4WXYLfwqsfr6z/HdCx7LqT9M7JktGob8WNgj8nFB4TbGNE4Cog== + dependencies: + undici-types "~5.26.4" + +"@types/retry@0.12.0": + version "0.12.0" + resolved "https://registry.yarnpkg.com/@types/retry/-/retry-0.12.0.tgz#2b35eccfcee7d38cd72ad99232fbd58bffb3c84d" + integrity sha512-wWKOClTTiizcZhXnPY4wikVAwmdYHp8q6DmC+EJUzAMsycb7HB32Kh9RN4+0gExjmPmZSAQjgURXIGATPegAvA== + "@types/sax@^1.2.1": version "1.2.7" resolved "https://registry.npmjs.org/@types/sax/-/sax-1.2.7.tgz" @@ -541,10 +605,24 @@ resolved "https://registry.npmjs.org/@types/unist/-/unist-2.0.6.tgz" integrity sha512-PBjIUxZHOuj0R15/xuwJYjFi+KZdNFrehocChv4g5hu6aFroHue8m0lBP0POdK2nKzbw0cgV1mws8+V/JAcEkQ== -agent-base@^7.1.2: - version "7.1.3" - resolved "https://registry.npmjs.org/agent-base/-/agent-base-7.1.3.tgz" - integrity sha512-jRR5wdylq8CkOe6hei19GGZnxM6rBGwFl3Bg0YItGDimvjGtAvdZk4Pu6Cl4u4Igsws4a1fd1Vq3ezrhn4KmFw== +"@types/uuid@^10.0.0": + version "10.0.0" + resolved "https://registry.yarnpkg.com/@types/uuid/-/uuid-10.0.0.tgz#e9c07fe50da0f53dc24970cca94d619ff03f6f6d" + integrity sha512-7gqG38EyHgyP1S+7+xomFtL+ZNHcKv6DwNaCZmJmo1vgMugyF3TCnXVg4t1uk89mLNwnLtnY3TpOpCOyp1/xHQ== + +abort-controller@^3.0.0: + version "3.0.0" + resolved "https://registry.yarnpkg.com/abort-controller/-/abort-controller-3.0.0.tgz#eaf54d53b62bae4138e809ca225c8439a6efb392" + integrity sha512-h8lQ8tacZYnR3vNQTgibj+tODHI5/+l06Au2Pcriv/Gmet0eaj4TwWH41sO9wnHDiQsEj19q0drzdWdeAHtweg== + dependencies: + event-target-shim "^5.0.0" + +agentkeepalive@^4.2.1: + version "4.6.0" + resolved "https://registry.yarnpkg.com/agentkeepalive/-/agentkeepalive-4.6.0.tgz#35f73e94b3f40bf65f105219c623ad19c136ea6a" + integrity sha512-kja8j7PjmncONqaTsB8fQ+wE2mSU2DJ9D4XKoJ5PFWIdRMa6SLSN1ff4mOr4jCbfRSsxR4keIiySJU0N9T5hIQ== + dependencies: + humanize-ms "^1.2.1" ajv@^8.0.1: version "8.11.0" @@ -580,6 +658,11 @@ ansi-styles@^4.0.0, ansi-styles@^4.1.0: dependencies: color-convert "^2.0.1" +ansi-styles@^5.0.0: + version "5.2.0" + resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-5.2.0.tgz#07449690ad45777d1924ac2abb2fc8895dba836b" + integrity sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA== + anymatch@~3.1.2: version "3.1.2" resolved "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz" @@ -644,6 +727,11 @@ async@^2.0.1: dependencies: lodash "^4.17.14" +asynckit@^0.4.0: + version "0.4.0" + resolved "https://registry.yarnpkg.com/asynckit/-/asynckit-0.4.0.tgz#c79ed97f7f34cb8f2ba1bc9790bcc366474b4b79" + integrity sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q== + available-typed-arrays@^1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz" @@ -660,16 +748,11 @@ balanced-match@^1.0.0: resolved "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz" integrity sha1-ibTRmasr7kneFk6gK4nORi1xt2c= -base64-js@^1.3.0: +base64-js@^1.5.1: version "1.5.1" resolved "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz" integrity sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA== -bignumber.js@^9.0.0: - version "9.2.1" - resolved "https://registry.npmjs.org/bignumber.js/-/bignumber.js-9.2.1.tgz" - integrity sha512-+NzaKgOUvInq9TIUZ1+DRspzf/HApkCwD4btfuasFTdrfnOxqx853TgDpMolp+uv4RpRp7bPcEU2zKr9+fRmyw== - binary-extensions@^2.0.0: version "2.2.0" resolved "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz" @@ -695,15 +778,18 @@ braces@^3.0.3, braces@~3.0.2: dependencies: fill-range "^7.1.1" -buffer-equal-constant-time@1.0.1: - version "1.0.1" - resolved "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz" - integrity sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA== - builtin-modules@^1.0.0: version "1.1.1" resolved "https://registry.npmjs.org/builtin-modules/-/builtin-modules-1.1.1.tgz" +call-bind-apply-helpers@^1.0.1, call-bind-apply-helpers@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz#4b5428c222be985d79c3d82657479dbe0b59b2d6" + integrity sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: version "1.0.7" resolved "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz" @@ -715,6 +801,11 @@ call-bind@^1.0.2, call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: get-intrinsic "^1.2.4" set-function-length "^1.2.1" +camelcase@6: + version "6.3.0" + resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-6.3.0.tgz#5685b95eb209ac9c0c177467778c9c84df58ba9a" + integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA== + ccount@^1.0.0, ccount@^1.0.3: version "1.1.0" resolved "https://registry.npmjs.org/ccount/-/ccount-1.1.0.tgz" @@ -801,6 +892,13 @@ color-name@~1.1.4: resolved "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz" integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA== +combined-stream@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-1.0.8.tgz#c3d45a8b34fd730631a110a8a2520682b31d5a7f" + integrity sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg== + dependencies: + delayed-stream "~1.0.0" + comma-separated-tokens@^1.0.0: version "1.0.8" resolved "https://registry.npmjs.org/comma-separated-tokens/-/comma-separated-tokens-1.0.8.tgz" @@ -820,6 +918,13 @@ consola@3.2.3: resolved "https://registry.npmjs.org/consola/-/consola-3.2.3.tgz" integrity sha512-I5qxpzLv+sJhTVEoLYNcTW+bThDCPsit0vLNKShZx6rLtpilNpmmeTPaeqJb9ZE9dV3DGaeby6Vuhrw38WjeyQ== +console-table-printer@^2.12.1: + version "2.14.3" + resolved "https://registry.yarnpkg.com/console-table-printer/-/console-table-printer-2.14.3.tgz#d679794a3dead5ba739517d52d90dedea426ed07" + integrity sha512-X5OCFnjYlXzRuC8ac5hPA2QflRjJvNKJocMhlnqK/Ap7q3DHXr0NJ0TGzwmEKOiOdJrjsSwEd0m+a32JAYPrKQ== + dependencies: + simple-wcswidth "^1.0.1" + cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz" @@ -861,13 +966,18 @@ data-view-byte-offset@^1.0.0: es-errors "^1.3.0" is-data-view "^1.0.1" -debug@4, debug@^4.0.0, debug@^4.3.4: +debug@^4.0.0, debug@^4.3.4: version "4.3.4" resolved "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz" integrity sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ== dependencies: ms "2.1.2" +decamelize@1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" + integrity sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA== + deep-is@^0.1.3: version "0.1.4" resolved "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz" @@ -899,6 +1009,11 @@ define-property@^2.0.2: is-descriptor "^1.0.2" isobject "^3.0.1" +delayed-stream@~1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" + integrity sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ== + diff@^4.0.1: version "4.0.2" resolved "https://registry.npmjs.org/diff/-/diff-4.0.2.tgz" @@ -914,12 +1029,14 @@ doublearray@0.0.2: resolved "https://registry.npmjs.org/doublearray/-/doublearray-0.0.2.tgz" integrity sha512-aw55FtZzT6AmiamEj2kvmR6BuFqvYgKZUkfQ7teqVRNqD5UE0rw8IeW/3gieHNKQ5sPuDKlljWEn4bzv5+1bHw== -ecdsa-sig-formatter@1.0.11, ecdsa-sig-formatter@^1.0.11: - version "1.0.11" - resolved "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz" - integrity sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ== +dunder-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/dunder-proto/-/dunder-proto-1.0.1.tgz#d7ae667e1dc83482f8b70fd0f6eefc50da30f58a" + integrity sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A== dependencies: - safe-buffer "^5.0.1" + call-bind-apply-helpers "^1.0.1" + es-errors "^1.3.0" + gopd "^1.2.0" emoji-regex@^10.1.0: version "10.3.0" @@ -996,6 +1113,11 @@ es-define-property@^1.0.0: dependencies: get-intrinsic "^1.2.4" +es-define-property@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.1.tgz#983eb2f9a6724e9303f61addf011c72e09e0b0fa" + integrity sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g== + es-errors@^1.2.1, es-errors@^1.3.0: version "1.3.0" resolved "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz" @@ -1008,6 +1130,13 @@ es-object-atoms@^1.0.0: dependencies: es-errors "^1.3.0" +es-object-atoms@^1.1.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.1.1.tgz#1c4f2c4837327597ce69d2ca190a7fdd172338c1" + integrity sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA== + dependencies: + es-errors "^1.3.0" + es-set-tostringtag@^2.0.3: version "2.0.3" resolved "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz" @@ -1017,6 +1146,16 @@ es-set-tostringtag@^2.0.3: has-tostringtag "^1.0.2" hasown "^2.0.1" +es-set-tostringtag@^2.1.0: + version "2.1.0" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz#f31dbbe0c183b00a6d26eb6325c810c0fd18bd4d" + integrity sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA== + dependencies: + es-errors "^1.3.0" + get-intrinsic "^1.2.6" + has-tostringtag "^1.0.2" + hasown "^2.0.2" + es-to-primitive@^1.2.1: version "1.2.1" resolved "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz" @@ -1071,6 +1210,16 @@ esprima@^4.0.0: resolved "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz" integrity sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A== +event-target-shim@^5.0.0: + version "5.0.1" + resolved "https://registry.yarnpkg.com/event-target-shim/-/event-target-shim-5.0.1.tgz#5d4d3ebdf9583d63a5333ce2deb7480ab2b05789" + integrity sha512-i/2XbnSz/uxRCU6+NdVJgKWDTM427+MqYbkQzD321DuCQJUqOuJKIA0IM2+W2xtYHdKOmZ4dR6fExsd4SXL+WQ== + +eventemitter3@^4.0.4: + version "4.0.7" + resolved "https://registry.yarnpkg.com/eventemitter3/-/eventemitter3-4.0.7.tgz#2de9b68f6528d5644ef5c59526a1b4a07306169f" + integrity sha512-8guHBZCwKnFhYdHr2ysuRWErTwhoN2X8XELRlrRwpmfeY2jjuUN4taQMsULKUVo1K4DvZl+0pgfyoysHxvmvEw== + execa@^9.3.0: version "9.3.0" resolved "https://registry.npmjs.org/execa/-/execa-9.3.0.tgz" @@ -1111,7 +1260,7 @@ extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@^3.0.0, extend@^3.0.2: +extend@^3.0.0: version "3.0.2" resolved "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== @@ -1204,11 +1353,35 @@ for-each@^0.3.3: dependencies: is-callable "^1.1.3" +form-data-encoder@1.7.2: + version "1.7.2" + resolved "https://registry.yarnpkg.com/form-data-encoder/-/form-data-encoder-1.7.2.tgz#1f1ae3dccf58ed4690b86d87e4f57c654fbab040" + integrity sha512-qfqtYan3rxrnCk1VYaA4H+Ms9xdpPqvLZa6xmMgFvhO32x7/3J/ExcTd6qpxM0vH2GdMI+poehyBZvqfMTto8A== + +form-data@^4.0.0: + version "4.0.3" + resolved "https://registry.yarnpkg.com/form-data/-/form-data-4.0.3.tgz#608b1b3f3e28be0fccf5901fc85fb3641e5cf0ae" + integrity sha512-qsITQPfmvMOSAdeyZ+12I1c+CKSstAFAwu+97zrnWAbIr5u8wfsExUzCesVLC8NgHuRUqNN4Zy6UPWUTRGslcA== + dependencies: + asynckit "^0.4.0" + combined-stream "^1.0.8" + es-set-tostringtag "^2.1.0" + hasown "^2.0.2" + mime-types "^2.1.12" + format@^0.2.0: version "0.2.2" resolved "https://registry.npmjs.org/format/-/format-0.2.2.tgz" integrity sha512-wzsgA6WOq+09wrU1tsJ09udeR/YZRaeArL9e1wPbFg3GG2yDnC2ldKpxs4xunpFF9DgqCqOIra3bc1HWrJ37Ww== +formdata-node@^4.3.2: + version "4.4.1" + resolved "https://registry.yarnpkg.com/formdata-node/-/formdata-node-4.4.1.tgz#23f6a5cb9cb55315912cbec4ff7b0f59bbd191e2" + integrity sha512-0iirZp3uVDjVGt9p49aTaqjk84TrglENEDuqfdlZQ1roC9CWlPk6Avf8EEnZNcAqPonwkG35x4n3ww/1THYAeQ== + dependencies: + node-domexception "1.0.0" + web-streams-polyfill "4.0.0-beta.3" + fs.realpath@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz" @@ -1244,26 +1417,6 @@ functions-have-names@^1.2.3: resolved "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz" integrity sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ== -gaxios@^6.0.0, gaxios@^6.1.1: - version "6.7.1" - resolved "https://registry.npmjs.org/gaxios/-/gaxios-6.7.1.tgz" - integrity sha512-LDODD4TMYx7XXdpwxAVRAIAuB0bzv0s+ywFonY46k126qzQHT9ygyoa9tncmOiQmmDrik65UYsEkv3lbfqQ3yQ== - dependencies: - extend "^3.0.2" - https-proxy-agent "^7.0.1" - is-stream "^2.0.0" - node-fetch "^2.6.9" - uuid "^9.0.1" - -gcp-metadata@^6.1.0: - version "6.1.1" - resolved "https://registry.npmjs.org/gcp-metadata/-/gcp-metadata-6.1.1.tgz" - integrity sha512-a4tiq7E0/5fTjxPAaH4jpjkSv/uCaU2p5KC6HVGrvl0cDjA8iBZv4vv1gyzlmK0ZUKqwpOyQMKzZQe3lTit77A== - dependencies: - gaxios "^6.1.1" - google-logging-utils "^0.0.2" - json-bigint "^1.0.0" - get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: version "1.2.4" resolved "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz" @@ -1275,6 +1428,30 @@ get-intrinsic@^1.1.3, get-intrinsic@^1.2.1, get-intrinsic@^1.2.3, get-intrinsic@ has-symbols "^1.0.3" hasown "^2.0.0" +get-intrinsic@^1.2.6: + version "1.3.0" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.3.0.tgz#743f0e3b6964a93a5491ed1bffaae054d7f98d01" + integrity sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ== + dependencies: + call-bind-apply-helpers "^1.0.2" + es-define-property "^1.0.1" + es-errors "^1.3.0" + es-object-atoms "^1.1.1" + function-bind "^1.1.2" + get-proto "^1.0.1" + gopd "^1.2.0" + has-symbols "^1.1.0" + hasown "^2.0.2" + math-intrinsics "^1.1.0" + +get-proto@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/get-proto/-/get-proto-1.0.1.tgz#150b3f2743869ef3e851ec0c49d15b1d14d00ee1" + integrity sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g== + dependencies: + dunder-proto "^1.0.1" + es-object-atoms "^1.0.0" + get-stdin@^5.0.1: version "5.0.1" resolved "https://registry.npmjs.org/get-stdin/-/get-stdin-5.0.1.tgz" @@ -1342,23 +1519,6 @@ globby@14.0.2: slash "^5.1.0" unicorn-magic "^0.1.0" -google-auth-library@^9.14.2: - version "9.15.1" - resolved "https://registry.npmjs.org/google-auth-library/-/google-auth-library-9.15.1.tgz" - integrity sha512-Jb6Z0+nvECVz+2lzSMt9u98UsoakXxA2HGHMCxh+so3n90XgYWkq5dur19JAJV7ONiJY22yBTyJB1TSkvPq9Ng== - dependencies: - base64-js "^1.3.0" - ecdsa-sig-formatter "^1.0.11" - gaxios "^6.1.1" - gcp-metadata "^6.1.0" - gtoken "^7.0.0" - jws "^4.0.0" - -google-logging-utils@^0.0.2: - version "0.0.2" - resolved "https://registry.npmjs.org/google-logging-utils/-/google-logging-utils-0.0.2.tgz" - integrity sha512-NEgUnEcBiP5HrPzufUkBzJOD/Sxsco3rLNo1F1TNf7ieU8ryUzBhqba8r756CjLX7rn3fHl6iLEwPYuqpoKgQQ== - gopd@^1.0.1: version "1.0.1" resolved "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz" @@ -1366,19 +1526,16 @@ gopd@^1.0.1: dependencies: get-intrinsic "^1.1.3" +gopd@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/gopd/-/gopd-1.2.0.tgz#89f56b8217bdbc8802bd299df6d7f1081d7e51a1" + integrity sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg== + graceful-fs@^4.1.2: version "4.1.15" resolved "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.1.15.tgz" integrity sha512-6uHUhOPEBgQ24HM+r6b/QwWfZq+yiFcipKFrOFiBEnWdy5sdzYoi+pJeQaPI5qOLRFqWmAXUPQNsielzdLoecA== -gtoken@^7.0.0: - version "7.1.0" - resolved "https://registry.npmjs.org/gtoken/-/gtoken-7.1.0.tgz" - integrity sha512-pCcEwRi+TKpMlxAQObHDQ56KawURgyAf6jtIY046fJ5tIv3zDe/LEIubckAO8fj6JnAxLdmWkUfNyulQ2iKdEw== - dependencies: - gaxios "^6.0.0" - jws "^4.0.0" - has-bigints@^1.0.1, has-bigints@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/has-bigints/-/has-bigints-1.0.2.tgz" @@ -1406,6 +1563,11 @@ has-symbols@^1.0.2, has-symbols@^1.0.3: resolved "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz" integrity sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A== +has-symbols@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.1.0.tgz#fc9c6a783a084951d0b971fe1018de813707a338" + integrity sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ== + has-tostringtag@^1.0.0, has-tostringtag@^1.0.2: version "1.0.2" resolved "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz" @@ -1451,19 +1613,18 @@ hosted-git-info@^2.1.4: resolved "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-2.8.9.tgz" integrity sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw== -https-proxy-agent@^7.0.1: - version "7.0.6" - resolved "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz" - integrity sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw== - dependencies: - agent-base "^7.1.2" - debug "4" - human-signals@^7.0.0: version "7.0.0" resolved "https://registry.npmjs.org/human-signals/-/human-signals-7.0.0.tgz" integrity sha512-74kytxOUSvNbjrT9KisAbaTZ/eJwD/LrbM/kh5j0IhPuJzwuA19dWvniFGwBzN9rVjg+O/e+F310PjObDXS+9Q== +humanize-ms@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/humanize-ms/-/humanize-ms-1.2.1.tgz#c46e3159a293f6b896da29316d8b6fe8bb79bbed" + integrity sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ== + dependencies: + ms "^2.0.0" + ignore@^5.2.4: version "5.3.1" resolved "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz" @@ -1685,11 +1846,6 @@ is-shared-array-buffer@^1.0.2, is-shared-array-buffer@^1.0.3: dependencies: call-bind "^1.0.7" -is-stream@^2.0.0: - version "2.0.1" - resolved "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz" - integrity sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg== - is-stream@^4.0.1: version "4.0.1" resolved "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz" @@ -1757,6 +1913,13 @@ japanese-numerals-to-number@^1.0.2: resolved "https://registry.npmjs.org/japanese-numerals-to-number/-/japanese-numerals-to-number-1.0.2.tgz" integrity sha512-rgs/V7G8Gfy8Z4XtnVBYXzWMAb9oUWp1pDdRmwHmh0hcjcy1kOu+DOpC5rwoHUAN4TqANwb7WD6z5W2v7v7PQQ== +js-tiktoken@^1.0.12: + version "1.0.20" + resolved "https://registry.yarnpkg.com/js-tiktoken/-/js-tiktoken-1.0.20.tgz#fa2733bf147acaf1bdcf9ab8a878e79c581c95f2" + integrity sha512-Xlaqhhs8VfCd6Sh7a1cFkZHQbYTLCwVJJWiHVxBYzLPxW0XsoxBy1hitmjkdIjD3Aon5BXLHFwU5O8WUx6HH+A== + dependencies: + base64-js "^1.5.1" + js-yaml@^3.14.1: version "3.14.1" resolved "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz" @@ -1780,13 +1943,6 @@ js-yaml@^4.1.0: dependencies: argparse "^2.0.1" -json-bigint@^1.0.0: - version "1.0.0" - resolved "https://registry.npmjs.org/json-bigint/-/json-bigint-1.0.0.tgz" - integrity sha512-SiPv/8VpZuWbvLSMtTDU8hEfrZWg/mH/nV/b4o0CYbSxu1UIQPLdwKOCIyLQX+VIPO5vrLX3i8qtqFyhdPSUSQ== - dependencies: - bignumber.js "^9.0.0" - json-parse-better-errors@^1.0.1: version "1.0.2" resolved "https://registry.npmjs.org/json-parse-better-errors/-/json-parse-better-errors-1.0.2.tgz" @@ -1801,22 +1957,10 @@ json5@^2.2.2: resolved "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz" integrity sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg== -jwa@^2.0.0: - version "2.0.0" - resolved "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz" - integrity sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA== - dependencies: - buffer-equal-constant-time "1.0.1" - ecdsa-sig-formatter "1.0.11" - safe-buffer "^5.0.1" - -jws@^4.0.0: - version "4.0.0" - resolved "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz" - integrity sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg== - dependencies: - jwa "^2.0.0" - safe-buffer "^5.0.1" +jsonpointer@^5.0.1: + version "5.0.1" + resolved "https://registry.yarnpkg.com/jsonpointer/-/jsonpointer-5.0.1.tgz#2110e0af0900fd37467b5907ecd13a7884a1b559" + integrity sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ== kuromoji@0.1.2: version "0.1.2" @@ -1835,6 +1979,36 @@ kuromojin@^3.0.0: kuromoji "0.1.2" lru_map "^0.4.1" +langchain@0.3.28: + version "0.3.28" + resolved "https://registry.yarnpkg.com/langchain/-/langchain-0.3.28.tgz#e3934ccb31930fe0121209a89523d118b60987d1" + integrity sha512-h4GGlBJNGU/Sj2PipW9kL+ewj7To3c+SnnNKH3HZaVHEqGPMHVB96T1lLjtCLcZCyUfabMr/zFIkLNI4War+Xg== + dependencies: + "@langchain/openai" ">=0.1.0 <0.6.0" + "@langchain/textsplitters" ">=0.0.0 <0.2.0" + js-tiktoken "^1.0.12" + js-yaml "^4.1.0" + jsonpointer "^5.0.1" + langsmith "^0.3.29" + openapi-types "^12.1.3" + p-retry "4" + uuid "^10.0.0" + yaml "^2.2.1" + zod "^3.25.32" + +langsmith@^0.3.29: + version "0.3.31" + resolved "https://registry.yarnpkg.com/langsmith/-/langsmith-0.3.31.tgz#ffb1164b88c2ef56f78934667b8cb4f6ada4fcbf" + integrity sha512-9lwuLZuN3tXFYQ6eMg0rmbBw7oxQo4bu1NYeylbjz27bOdG1XB9XNoxaiIArkK4ciLdOIOhPMBXP4bkvZOgHRw== + dependencies: + "@types/uuid" "^10.0.0" + chalk "^4.1.2" + console-table-printer "^2.12.1" + p-queue "^6.6.2" + p-retry "4" + semver "^7.6.3" + uuid "^10.0.0" + levn@^0.4.1: version "0.4.1" resolved "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz" @@ -1919,6 +2093,11 @@ match-index@^1.0.1, match-index@^1.0.3: dependencies: regexp.prototype.flags "^1.1.1" +math-intrinsics@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/math-intrinsics/-/math-intrinsics-1.1.0.tgz#a0dd74be81e2aa5c2f27e65ce283605ee4e2b7f9" + integrity sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g== + md5@^2.3.0: version "2.3.0" resolved "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz" @@ -2102,6 +2281,18 @@ micromatch@^4.0.4: braces "^3.0.3" picomatch "^2.3.1" +mime-db@1.52.0: + version "1.52.0" + resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.52.0.tgz#bbabcdc02859f4987301c856e3387ce5ec43bf70" + integrity sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg== + +mime-types@^2.1.12: + version "2.1.35" + resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.35.tgz#381a871b62a734450660ae3deee44813f70d959a" + integrity sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw== + dependencies: + mime-db "1.52.0" + minimatch@^3.1.1: version "3.1.2" resolved "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz" @@ -2153,7 +2344,22 @@ ms@2.1.2: resolved "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz" integrity sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w== -node-fetch@^2.6.9: +ms@^2.0.0: + version "2.1.3" + resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" + integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== + +mustache@^4.2.0: + version "4.2.0" + resolved "https://registry.yarnpkg.com/mustache/-/mustache-4.2.0.tgz#e5892324d60a12ec9c2a73359edca52972bf6f64" + integrity sha512-71ippSywq5Yb7/tVYyGbkBggbU8H3u5Rz56fH60jGFgr8uHwxs+aSKeqmluIVzM0m0kB7xQjKS6qPfd0b2ZoqQ== + +node-domexception@1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/node-domexception/-/node-domexception-1.0.0.tgz#6888db46a1f71c0b76b3f7555016b63fe64766e5" + integrity sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ== + +node-fetch@^2.6.7: version "2.7.0" resolved "https://registry.npmjs.org/node-fetch/-/node-fetch-2.7.0.tgz" integrity sha512-c4FRfUm/dbcWZ7U+1Wq0AwCyFL+3nt2bEw05wfxSz+DWpWsitgmSgYmy2dQdWyKC1694ELPqMs/YzUSNozLt8A== @@ -2213,6 +2419,24 @@ once@^1.3.0: dependencies: wrappy "1" +openai@^4.96.0: + version "4.104.0" + resolved "https://registry.yarnpkg.com/openai/-/openai-4.104.0.tgz#c489765dc051b95019845dab64b0e5207cae4d30" + integrity sha512-p99EFNsA/yX6UhVO93f5kJsDRLAg+CTA2RBqdHK4RtK8u5IJw32Hyb2dTGKbnnFmnuoBv5r7Z2CURI9sGZpSuA== + dependencies: + "@types/node" "^18.11.18" + "@types/node-fetch" "^2.6.4" + abort-controller "^3.0.0" + agentkeepalive "^4.2.1" + form-data-encoder "1.7.2" + formdata-node "^4.3.2" + node-fetch "^2.6.7" + +openapi-types@^12.1.3: + version "12.1.3" + resolved "https://registry.yarnpkg.com/openapi-types/-/openapi-types-12.1.3.tgz#471995eb26c4b97b7bd356aacf7b91b73e777dd3" + integrity sha512-N4YtSYJqghVu4iek2ZUvcN/0aqH1kRDuNqzcycDxhOUpg7GdvLa2F3DgS6yBNhInhv2r/6I0Flkn7CqL8+nIcw== + optionator@^0.9.3: version "0.9.3" resolved "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz" @@ -2225,6 +2449,11 @@ optionator@^0.9.3: prelude-ls "^1.2.1" type-check "^0.4.0" +p-finally@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/p-finally/-/p-finally-1.0.0.tgz#3fbcfb15b899a44123b34b6dcc18b724336a2cae" + integrity sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow== + p-limit@^1.1.0: version "1.3.0" resolved "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz" @@ -2239,6 +2468,29 @@ p-locate@^2.0.0: dependencies: p-limit "^1.1.0" +p-queue@^6.6.2: + version "6.6.2" + resolved "https://registry.yarnpkg.com/p-queue/-/p-queue-6.6.2.tgz#2068a9dcf8e67dd0ec3e7a2bcb76810faa85e426" + integrity sha512-RwFpb72c/BhQLEXIZ5K2e+AhgNVmIejGlTgiB9MzZ0e93GRvqZ7uSi0dvRF7/XIXDeNkra2fNHBxTyPDGySpjQ== + dependencies: + eventemitter3 "^4.0.4" + p-timeout "^3.2.0" + +p-retry@4: + version "4.6.2" + resolved "https://registry.yarnpkg.com/p-retry/-/p-retry-4.6.2.tgz#9baae7184057edd4e17231cee04264106e092a16" + integrity sha512-312Id396EbJdvRONlngUx0NydfrIQ5lsYu0znKVUzVvArzEIt08V1qhtyESbGVd1FGX7UKtiFp5uwKZdM8wIuQ== + dependencies: + "@types/retry" "0.12.0" + retry "^0.13.1" + +p-timeout@^3.2.0: + version "3.2.0" + resolved "https://registry.yarnpkg.com/p-timeout/-/p-timeout-3.2.0.tgz#c7e17abc971d2a7962ef83626b35d635acf23dfe" + integrity sha512-rhIwUycgwwKcP9yTOOFK/AKsAopjjCakVqLHePO3CC6Mir1Z99xT+R63jZxAT5lFZLa2inS5h+ZS2GvR99/FBg== + dependencies: + p-finally "^1.0.0" + p-try@^1.0.0: version "1.0.0" resolved "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz" @@ -2518,6 +2770,11 @@ ret@~0.1.10: resolved "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz" integrity sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg== +retry@^0.13.1: + version "0.13.1" + resolved "https://registry.yarnpkg.com/retry/-/retry-0.13.1.tgz#185b1587acf67919d63b357349e03537b2484658" + integrity sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg== + reusify@^1.0.4: version "1.0.4" resolved "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz" @@ -2554,11 +2811,6 @@ safe-array-concat@^1.1.2: has-symbols "^1.0.3" isarray "^2.0.5" -safe-buffer@^5.0.1: - version "5.2.1" - resolved "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz" - integrity sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ== - safe-regex-test@^1.0.3: version "1.0.3" resolved "https://registry.npmjs.org/safe-regex-test/-/safe-regex-test-1.0.3.tgz" @@ -2585,6 +2837,11 @@ sax@^1.2.4: resolved "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz" integrity sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g== +semver@^7.6.3: + version "7.7.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.7.2.tgz#67d99fdcd35cec21e6f8b87a7fd515a33f982b58" + integrity sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA== + sentence-splitter@^5.0.0: version "5.0.0" resolved "https://registry.npmjs.org/sentence-splitter/-/sentence-splitter-5.0.0.tgz" @@ -2642,6 +2899,11 @@ signal-exit@^4.1.0: resolved "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz" integrity sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw== +simple-wcswidth@^1.0.1: + version "1.1.1" + resolved "https://registry.yarnpkg.com/simple-wcswidth/-/simple-wcswidth-1.1.1.tgz#a96ff1b5cff660262ea33850e19a0e7249caed50" + integrity sha512-R3q3/eoeNBp24CNTASEUrffXi0j9TwPIEvSStlvSrsFimM17sV5EHcMOc86j3K+UWZyLYvH0hRmYGCpCoaJ4vw== + sitemap@8.0.0: version "8.0.0" resolved "https://registry.npmjs.org/sitemap/-/sitemap-8.0.0.tgz" @@ -3386,10 +3648,15 @@ uri-js@^4.2.2: dependencies: punycode "^2.1.0" -uuid@^9.0.1: - version "9.0.1" - resolved "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz" - integrity sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA== +uuid@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-10.0.0.tgz#5a95aa454e6e002725c79055fd42aaba30ca6294" + integrity sha512-8XkAphELsDnEGrDxUOHB3RGvXz6TeuYSGEZBOjtTtPm2lwhGBjLgOzLHB63IUWfBpNucQjND6d3AOudO+H3RWQ== + +uuid@^11.1.0: + version "11.1.0" + resolved "https://registry.yarnpkg.com/uuid/-/uuid-11.1.0.tgz#9549028be1753bb934fc96e2bca09bb4105ae912" + integrity sha512-0/A9rDy9P7cJ+8w1c9WD9V//9Wj15Ce2MPz8Ri6032usz+NfePxx5AcN3bN+r6ZL6jEo066/yNYB3tn4pQEx+A== validate-npm-package-license@^3.0.1: version "3.0.4" @@ -3421,6 +3688,11 @@ web-namespaces@^1.1.2: resolved "https://registry.npmjs.org/web-namespaces/-/web-namespaces-1.1.4.tgz" integrity sha512-wYxSGajtmoP4WxfejAPIr4l0fVh+jeMXZb08wNc0tMg6xsfZXj3cECqIK0G7ZAqUq0PP8WlMDtaOGVBTAWztNw== +web-streams-polyfill@4.0.0-beta.3: + version "4.0.0-beta.3" + resolved "https://registry.yarnpkg.com/web-streams-polyfill/-/web-streams-polyfill-4.0.0-beta.3.tgz#2898486b74f5156095e473efe989dcf185047a38" + integrity sha512-QW95TCTaHmsYfHDybGMwO5IJIM93I/6vTRk+daHTWFPhwh+C8Cg7j7XyKrwrj8Ib6vYXe0ocYNrmzY4xAAN6ug== + webidl-conversions@^3.0.0: version "3.0.1" resolved "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz" @@ -3475,16 +3747,16 @@ write@1.0.3: dependencies: mkdirp "^0.5.1" -ws@^8.18.0: - version "8.18.1" - resolved "https://registry.npmjs.org/ws/-/ws-8.18.1.tgz" - integrity sha512-RKW2aJZMXeMxVpnZ6bck+RswznaxmzdULiBr6KY7XkTnW8uvt0iT9H5DkHUChXrc+uurzwa0rVI16n/Xzjdz1w== - xtend@^4.0.0, xtend@^4.0.1: version "4.0.2" resolved "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz" integrity sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ== +yaml@^2.2.1: + version "2.8.0" + resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.8.0.tgz#15f8c9866211bdc2d3781a0890e44d4fa1a5fff6" + integrity sha512-4lLa/EcQCB0cJkyts+FpIRx5G/llPxfP6VQU5KByHEhLxY3IJCH0f0Hy1MHI8sClTvsIb8qwRJ6R/ZdlDJ/leQ== + yoctocolors@^2.0.0: version "2.1.1" resolved "https://registry.npmjs.org/yoctocolors/-/yoctocolors-2.1.1.tgz" @@ -3495,6 +3767,21 @@ zlibjs@^0.3.1: resolved "https://registry.npmjs.org/zlibjs/-/zlibjs-0.3.1.tgz" integrity sha512-+J9RrgTKOmlxFSDHo0pI1xM6BLVUv+o0ZT9ANtCxGkjIVCCUdx9alUF8Gm+dGLKbkkkidWIHFDZHDMpfITt4+w== +zod-to-json-schema@^3.22.3: + version "3.24.5" + resolved "https://registry.yarnpkg.com/zod-to-json-schema/-/zod-to-json-schema-3.24.5.tgz#d1095440b147fb7c2093812a53c54df8d5df50a3" + integrity sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g== + +zod@3.25.32: + version "3.25.32" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.32.tgz#769cc684072df780fc8f38130b0cd9283a8d3818" + integrity sha512-OSm2xTIRfW8CV5/QKgngwmQW/8aPfGdaQFlrGoErlgg/Epm7cjb6K6VEyExfe65a3VybUOnu381edLb0dfJl0g== + +zod@^3.25.32: + version "3.25.64" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.25.64.tgz#57b5c7e76dd64e447f7e710285fcdb396b32f803" + integrity sha512-hbP9FpSZf7pkS7hRVUrOjhwKJNyampPgtXKc3AN6DsWtoHsg2Sb4SQaS4Tcay380zSwd2VPo9G9180emBACp5g== + zwitch@^1.0.0: version "1.0.5" resolved "https://registry.npmjs.org/zwitch/-/zwitch-1.0.5.tgz"