Giter VIP home page Giter VIP logo

pluto-lang / pluto Goto Github PK

View Code? Open in Web Editor NEW
79.0 5.0 7.0 30.36 MB

Pluto provides a unified programming interface that allows you to seamlessly tap into cloud capabilities and develop your business logic.

Home Page: https://pluto-lang.vercel.app

License: Apache License 2.0

TypeScript 89.19% JavaScript 0.55% Shell 2.55% Python 7.62% Makefile 0.09%
iac programming-languages severless ifc programming help-wanted low-code ai deployment llm

pluto's Introduction



English | 简体中文

slack license npm version npm download

Pluto is a development tool dedicated to helping developers build cloud and AI applications more conveniently, resolving issues such as the challenging deployment of AI applications and open-source models.

Developers are able to write applications in familiar programming languages like Python and TypeScript, directly defining and utilizing the cloud resources necessary for the application within their code base, such as AWS SageMaker, DynamoDB, and more. Pluto automatically deduces the infrastructure resource needs of the app through static program analysis and proceeds to create these resources on the specified cloud platform, simplifying the resources creation and application deployment process.

⚠️ Caution: Pluto is still in its early stages, please consider it for production environments with caution.

🌟 Example

Let's develop a text generation application based on GPT2, where the user input is processed by the GPT2 model to generate and return text. Below is how the development process with Pluto looks:

GPT2 Process

AWS SageMaker is utilized as the model deployment platform, and AWS Api Gateway and Lambda support the application's HTTP services. The deployed application architecture, as shown in the top right graphic ↗️, comprises two route handling functions: one to receive user input, invoke the SageMaker model, and return generated text, and another to provide the endpoint URL of the SageMaker model.

The top left graphic ↖️ captures a fragment of the application code, with the complete code accessible here. In the TypeScript code development process using Pluto, by creating a new SageMaker instance using new SageMaker(), you can directly interact with the SageMaker model using methods like sagemaker.invoke() and obtain the model endpoint URL using sagemaker.endpointUrl(). Establishing an Api Gateway requires only creating a new variable router with new Router(), and the function arguments within the methods of router, such as router.get(), router.post(), etc., will automatically be converted into Lambda functions. The same application could be implemented in Python as well.

Once the application code has been written, executing pluto deploy allows Pluto to deduce the application's infrastructure needs and automatically provision around 30 cloud resources, which includes instances such as SageMaker, Lambda, Api Gateway, along with setups like triggers, IAM roles, and policy permissions.

Finally, Pluto hands back the URL of the Api Gateway, providing direct access to use the application.

Interested in exploring more examples?

🚀 Quick Start

Online Experience: CodeSandbox provides an online development environment. We have constructed Pluto templates in both Python and TypeScript languages on this platform, allowing direct experience in the browser. After opening the project template, creating your own project is as easy as clicking the Fork button in the top right corner. The environment is pre-equipped with AWS CLI, Pulumi, and Pluto's basic dependencies, adhering to the README for operations.

Container Experience: We offer a container image plutolang/pluto:latest for application development, which contains essential dependencies like AWS CLI, Pulumi, and Pluto, along with Node.js 20.x and Python 3.10 environments pre-configured. If you are interested in developing only TypeScript applications, you can use the plutolang/pluto:latest-typescript image. You can partake in Pluto development within a container using the following command:

docker run -it --name pluto-app plutolang/pluto:latest bash

Local Experience: For local use, please follow these steps for setup:

0. Install Pulumi

Pluto operates within a Node.js environment and uses Pulumi for interaction with cloud platforms (AWS or K8s). You can refer to the Pulumi installation guide.

1. Install Pluto

npm install -g @plutolang/cli

2. Deploy your application with Pluto

pluto new        # Interactively create a new project, allowing selection of TypeScript or Python
cd <project_dir> # Enter your project directory
npm install      # Download dependencies

# If it's a Python project, in addition to npm install, Python dependencies must also be installed.
pip install -r requirements.txt

pluto deploy     # Deploy with one click!

⚠️ Note:

  • If the target platform is AWS, Pluto attempts to read your AWS configuration file to acquire the default AWS Region, or alternatively, tries to fetch it from the environment variable AWS_REGION. Deployment will fail if neither is set.
  • If the target platform is Kubernetes, Knative must firstly be installed within K8s and the scale-to-zero feature should be deactivated (as Pluto doesn't yet support Ingress forwarding to Knative serving). You can configure the required Kubernetes environment following this document.

For detailed steps, refer to the Getting Started Guide.

Currently, Pluto only supports single-file configurations. Inside each handler function, access is provided to literal constants and plain functions outside of the handler's scope; however, Python allows direct access to classes, interfaces, etc., outside of the scope, whereas TypeScript requires encapsulating these within functions for access.

🤯 Pain Points

Here you can find out why Pluto was created. To put it simply, we aim to address several pain points you might often encounter:

  • High learning curve: Developing a cloud application requires mastery of both the business and infrastructure skills, and it often demands significant efforts in testing and debugging. Thus, developers spend a considerable amount of energy on aspects beyond writing the core business logic.
  • High cognitive load: With cloud service providers offering hundreds of capabilities and Kubernetes offering nearly limitless possibilities, average developers often lack a deep understanding of cloud infrastructure, making it challenging to choose the proper architecture for their particular needs.
  • Poor programming experience: Developers must maintain separate codebases for infrastructure and business logic or intertwine infrastructure configuration within the business logic, leading to a sub-optimal programming experience that falls short of the simplicity of creating a local standalone program.
  • Vendor lock-in: Coding for a specific cloud provider can lead to poor flexibility in the resulting code. When it becomes necessary to migrate to another cloud platform due to cost or other factors, adapting the existing code to the new environment can require substantial changes.

💡 Features

  • No learning curve: The programming interface is fully compatible with TypeScript, Python, and supports the majority of dependency libraries such as LangChain, LangServe, FastAPI, etc.
  • Focus on pure business logic: Developers only need to write the business logic. Pluto, via static analysis, automatically deduces the infrastructure requirements of the application.
  • One-click cloud deployment: The CLI provides basic capabilities such as compilation and deployment. Beyond coding and basic configuration, everything else is handled automatically by Pluto.
  • Support for various runtime environments: With a unified abstraction based on the SDK, it allows developers to migrate between different runtime environments without altering the source code.

🔧 How Does Pluto Work?

Pluto Architecture

Overall, the Pluto deployment process comprises three stages—deduction, generation, and deployment:

  1. Deduction Phase: The deducer analyzes the application code to derive the required cloud resources and their interdependencies, resulting in an architecture reference. It also splits user business code into business modules, which, along with the dependent SDK, form the business bundle.
  2. Generation Phase: The generator creates IaC code that is independent of user code, guided by the architecture reference.
  3. Deployment Phase: Depending on the IaC code type, Pluto invokes the corresponding adapter, which, in turn, works with the respective IaC engine to execute the IaC code, managing infrastructure configuration and application deployment.

Components such as the deducer, generator, and adapter are extendable, which allows support for a broader range of programming languages and platform integration methods. Currently, Pluto provides deducers for Python and TypeScript, and a generator and adapter for Pulumi. Learn more about Pluto's processes in detail in this document.

🤔️ Differences from Other Projects?

Pluto distinguishes itself from other offerings by leveraging static program analysis techniques to infer resource dependencies directly from application code and generate infrastructure code that remains separate from business logic. This approach ensures infrastructure configuration does not intrude into business logic, providing developers with a development experience free from infrastructure concerns.

  • Compared to BaaS (Backend as a Service) products like Supabase or Appwrite, Pluto assists developers in creating the necessary infrastructure environment within their own cloud account, rather than offering managed components.
  • Differing from PaaS (Platform as a Service) offerings like Fly.io, Render, Heroku, or LeptonAI, Pluto does not handle application hosting. Instead, it compiles application into finely-grained compute modules, and integrates with rich cloud platform capabilities like FaaS, GPU instances, and message queues, enabling deployment to cloud platforms without requiring developers to write extra configurations.
  • In contrast to scaffolding tools such as the Serverless Framework or Serverless Devs, Pluto does not impose an application programming framework specific to particular cloud providers or frameworks, but instead offers a uniform programming interface.
  • Unlike IfC (Infrastructure from Code) products based purely on annotations like Klotho, Pluto infers resource dependencies directly from user code, eliminating the need for extra annotations.
  • Different from other IfC products that rely on dynamic analysis, like Shuttle, Nitric, and Winglang, Pluto employs static program analysis to identify application resource dependencies, generating independent infrastructure code without having to execute user code.

You can learn more about the differences with other projects in this document.

👏 Contributing

Pluto is still in its infancy, and we warmly welcome contributions from those who are interested. Any suggestions or ideas about the issues Pluto aims to solve, the features it offers, or its code implementation can be shared and contributed to the community. Please refer to our project contribution guide for more information.

🐎 Roadmap

  • Complete implementation of the resource static deduction process
    • 🚧 Resource type checking
    • ❌ Conversion of local variables into cloud resources
  • SDK development
    • 🚧 Client SDK development
    • 🚧 Infra SDK development
    • ❌ Support for additional resources and more platforms
  • Engine extension support
    • 🚧 Pulumi
    • ❌ Terraform
  • 🚧 Local simulation and testing functionality

Please see the Issue list for further details.

📊 Capability Matrix

✅: Indicates that all user-visible interfaces are available
🚧: Indicates that some of the user-visible interfaces are available
❌: Indicates not yet supported

TypeScript

Resource Type AWS Kubernetes Alibaba Cloud Simulation
Router 🚧 🚧 🚧
Queue
KVStore
Function
Schedule
Tester
SageMaker

Python

Resource Type AWS Kubernetes Alibaba Cloud Simulation
Router
Queue
KVStore
Function
Schedule
Tester
SageMaker

💬 Community

Join our Slack community to communicate and contribute ideas.

pluto's People

Contributors

github-actions[bot] avatar jianzs avatar peefy avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

pluto's Issues

Move the process of exporting computing closure from the generator to the deducer

In the current implementation, Deducer deduces the positions of each FaaS instance within the code and records them into arch ref. Subsequently, it's generator's responsibility to export and compile each module's code.

After adopting the execution model of computing closures, deducer will analyze dependencies throughout the entire computing closure. This analysis process is similar to that of exporting code. Moving the process of exporting code to deducer can enhance its cohesion and simplify generator's implementation.

Support deploying Python projects across different environments

Currently, the deducer working with Python projects, Pyright deducer, can retrieve all the dependent modules for the specified compute closure. It then copies these modules to the directory where the closure is located. However, if the target environment differs from the local one – for example, if there's a different CPU architecture or operating system – the closures may not execute successfully.

Provide unique names for resources in different projects

Users have the ability to create two resources with identical names in different projects. It would be advantageous for users to ensure that each resource within different projects has a unique and easily identifiable name.

Move the timing of FaaS object creation to the Infra API

Problem Statement

During the Proof of Concept (PoC) phase, the approach to adapting to cloud platforms involved using a centralized runtime.ts file to analyze event types and then encapsulate them. Specifically, when a FaaS resource instance receives a resource request, it first enters the processing flow of runtime.ts. This file determines the type of request based on certain attributes of the event object, extracts and encapsulates the event according to the request type into parameters needed by the Handler function, and then calls the Handler to execute. This method mingles the adaptation code for various components within a single file, including the HTTP adaptation code for Routers, the Event adaptation code for Queues, etc., which leads to several issues:

  1. Contextual separation. SDK resource component developers create platform-specific Infra implementation classes for their resource types, but the code for adapting to the platform is in runtime.ts, separated from the Infra class; developers need to know where to add adaptation code. For example, Router resource types use AWS's ApiGateway, and while the relationship between Lambda and ApiGateway is built within the Router's implementation class, the code to adapt Lambda specifications and invoke the user's Handler is in runtime.ts.
  2. Resource component conflict. Differentiating request types based on event object property values is not always accurate, and when two components rely on the same property values, it necessitates adjustments to the logic in runtime.ts, resulting in high coupling.

Solution Approach

The early PoC method involved the Deducer stage outputting an Arch Ref that directly included FaaS resource instances. This approach has been modified so that the Deducer only deduces the compute closures contained within the application, without directly representing these closures as corresponding FaaS resource objects.

FaaS resource objects will be created by specific implementations of the BaaS Infra API. Within these implementations, adaptation code will be added on top of the compute closures based on their own functions and cloud platform specifications, forming a higher-level closure. Subsequently, a FaaS resource object is created to complete the FaaS resource instance's creation.

Implementation Approach

Basic Setup

Each exported computational closure will be saved in its own directory, containing an index.ts file that by default exports the closure's entry point. For example:

export default (a: number, b: number): number => {
	return a + b;
};

Under the core subdirectory of @plutolang/base, define the ComputeClosure interface:

interface Dependency {
	resourceObject: Resource;
	resourceType: string; // Format: package.type e.g. "@plutolang/pluto.Queue"
	type: "access" | "props";
}

interface ComputeClosure {
	filepath: string;  // Path to the directory of compute closure 
	dependencies?: Dependency[]; // The resource objects that the compute closure relies on, which includes two types of dependencies: Client API calls and Property accesses.
}

Generator Output

When generating IaC code, the generator imports the closure and configures its properties based on the arch ref. For example:

const router = new Router("foo");

import { default as closure } from "./path/to/closure";
const closure_1 = closure as ComputeClosure & (typeof closure);
closure_1.filepath = "./path/to/closure";

closure_1.dependencies = []
closure_1.dependencies.push({ 
  resourceObject: router, 
  resourceType: "@plutolang/pluto.Router", 
  type: "props", 
  method: "url" 
})

router.get("/", closure_1);

Infra SDK Development

When implementing the Infra API for a resource type in the Infra SDK, it's necessary to check if the Handler is of ComputeClosure type. If it is, the dependencies must be passed to the FaaS resource object during its construction. For example:

class Router {
  public get(path: string, handler: RouterEndpointHandler) {
    const dependencies = isComputeClosure(handler) ? handler.dependencies : undefined;
    const fn = new Function(wrapper, { dependencies });
    // do something...

    function wrapper(event, context) {
      // adapt the platform specification.
      // And then, call the handler function.
      handler(/* arguments */);
    }
  }
}

In the constructor of the FaaS resource type Function, it's also necessary to determine whether the incoming handler is of ComputeClosure type. If it is, this indicates that the user has created a Function resource object directly in the code, with ComputeClosure attributes filled in by the generator. Otherwise, it signifies that a Function resource object was constructed in the SDK, as shown in the code above. For example:

class Function {
  constructor(fn: FunctionHandler, opts: FunctionOptions) {
    const dependencies = opts.dependencies ?? [];
    if (isComputeClosure(fn) && fn.dependencies) {
      // The user creates this function directly in the code.
      dependencies.push(...fn.dependencies)
    }


    // extract envrionment variables list from dependencies
    const lambda = new aws.lambda.Function(/* ... envs */);

    // construct permission policies list from dependencies
    lambda.addPermission(/* policies */)
  }
}

Benefits

This approach addresses the issues mentioned earlier: developers need only focus on adaptation code within the Infra API of the resource type, and the adaptation code for different resource types is unrelated.

Moreover, the timing of FaaS resource object creation is decided by the Infra class of the BaaS resource type, allowing the Infra class to dictate the granularity of the code executed in the FaaS resource instance. For instance, multiple compute closures could be combined, and then only one FaaS resource object is created and deployed to a FaaS resource instance.

Existing Challenges

SDK developers need to understand the concept of compute closures, and when developing the SDK, they need to recognize that the object passed to them could be of ComputeClosure type. Upon confirmation, developers need to actively add dependencies to the Function's Options parameter.

Add Terraform adapter

We need to create a Terraform adapter that enables the development of the infrastructure SDK using Terraform. The initial idea is to use CDK-TF for developing the Infra SDKs. The generator will produce platform code that imports the Infra SDKs. This adapter will run the generated platform code to produce a Terraform file, and then execute this file to deploy the infrastructure.

To implement this adapter, you can refer to the implementation of the Pulumi adapter.

Design and implement the function encapsulation mechanism

Describe the feature

To support users in encapsulating the process of reusing infrastructure configuration through functions.

Use Case

I am creating a Chatbot based on LLMs (Large Language Models), using Pluto to build the application's backend, which supports multiple LLMs. Each LLM has two interfaces: 1) /{model}/info to get the basic information of the current LLM, and 2) /{model}/invoke to call the LLM. To reuse the process of configuring routes, it is necessary to implement a function to configure routes for a single LLM, and call this function multiple times to configure routes for each LLM.

Proposed Solution

No response

Other Information

Details are available at https://github.com/pluto-lang/pluto/blob/main/docs/documentation/design/function-encapsulation.en.mdx

Acknowledgements

  • I may be able to implement this feature request
  • This feature might incur a breaking change

Support plugin mechanism

Objective

To meet requirements such as cost control and global network configurations, there is a need for a method to modify the generated IaC code. These types of requirements are best represented as rules and implemented as plugins.

Usage: Plugin Configuration

Plugins are scoped to a stack. Within a stack, the plugins field can be added to configure multiple plugins in a list. For instance, setting up a custom domain or VPC configuration might look like this:

stacks:
- name: dev
  # other configurations...
  plugins:
  - package: add-custom-domain
    configs:
      domain: foo
      type: foo
  - package: add-vpc
    configs:
      cidr: 10.0.0.0/16

Each plugin contains two parts: package and configs. The package specifies the plugin's package name or can directly point to a file. configs are the plugin's related configurations, which are passed to the plugin's constructor during its construction.

The plugin can be an npm package or a JavaScript file. There is also the possibility of supporting plugins written in multiple languages, for example, using the mutation capabilities of KCL.

Implementation

To address the need to modify IaC code, after generating the IaC code, actively invoke each plugin in sequence, maintaining the input and output format consistent with the output of the generator.

In the future, there might be more configurability requirements within the entire Pluto workflow. It would be possible to set multiple events throughout the lifecycle of the workflow, such as before:deduce, after:generate, etc. Plugins would register hooks for the events they are interested in, and Pluto would trigger the corresponding hooks.

AI X Serverless X Programming Language

Goals

Using a unified programming interface and workspace to unify abstract applications and infrastructure, further abstracting this programming interface through natural language through AI to combine application backend and infrastructure code to quickly build and deliver cloud applications.

The project name maybe

  • fly-lang/fly
  • air-lang/air
  • ark-lang/ark

Pain Points

  • Infrastructure such as cloud and Kubernetes is becoming increasingly complex, and existing IaC tools such as terraform, cdk, help, etc. cannot and require a more advanced abstraction. Developers inevitably fall into a fragmented process between application and IaC.
  • Even existing Serverless products such as AWS Lambda require developers to handle these processes. For example, when executing code within AWS Lambda functions, I must understand that the required dependencies are bundled together, uploaded as a zip file to S3 and deployed through Terraform (or manually uploaded, which is very boring and container error prone).
  • The same applies to Kubernetes Serverless products such as Knative. Not only do I need to understand the installation and use of Knative, but I also need to install the MySQL database on K8s myself and hardcode it into my code. When I was developing, Knative was unable to provide me with assistance, which required me to maintain two databases and ensure that when I went online, I used the prod database. Moreover, to understand the more specific operation of the product, it is necessary to deploy components such as Grafana, Prometheus, Fluent bit, and enable them to coordinate their work. I would like to switch to the MySQL service provided by Alibaba Cloud in the future, and I also need to modify my source code and republish it.
  • Existing next-generation IaC tools such as Winglang or Infra from Code technology either lean towards IaC, making it difficult to write code that meets the requirements due to fragmented programming experiences, or write Infra through annotation configuration. In fact, application development users should not have cared about these things, they should simply import cloud APIs to implement their functions. When I build professional software, I want most of my time to be spent within the functional domain of my application, instead of non-functional mechanics of the platform I use.
  • Pain Points of Application Runtime e.g., Layotto, it may lose terraform/pulumi integration. mosn/layotto#976

User Story

As a developer, I just want to develop and complete my application (usually some server applications or FaaS functions) without installation, understanding and writing IaC, or even understanding infrastructure concepts such as containers, or understanding and switching complex environments or permissions. Everything that follows can be handed over to automation. I only see my application work and easily complete observable tasks such as log and metric. For example, as the developer of kcl playground, every time I develop an upgraded version of kcl playground and create a Docker image, I always have to click on the platform or write terraform code to automatically send it. These things are boring and I don't want to do them and I don't want to learn docker and terraform/pulumi.

Overview

image

image

  • One Infra and App Config Abstraction including APIs e.g. storage, function etc.
    • Infra: Multi-Cloud and Kubernetes Abstraction
    • Solving App Dependency on Infra by Combining Abstract Methods
  • Deploy-less and No configuration Code
    • There are no infra related configuration codes in the user interface, and we need to use intermediate generation to hide them.
  • API Registry
    • Through centralized management of APIs, one is to reduce the cost of API production and consumption, and the other is to use fine-tuning data as AI generation models.

Features

image

Design

Engine

image

Workflow

image
image

API

image

Roadmap

  • 2023.08: Make a decision: POC idea and big direction set, name
  • 2023.09: Top level design confirmation and PoC start: Expect to provide developers with programming interface and engine layer capabilities, selling points, problem solving, targeted users, boundaries, scenarios, etc. can be determined, and specific synchronization solutions and coding implementation can start
  • 2023.10: Feasibility verification: feasible for a scenario/workload
  • 2023.11: PoC improvement: Horizontal expansion and iteration of some capabilities based on PoC, laying the groundwork for some scenarios in December
  • 2023.12: Horizontal Scenario Improvement: Prepare some materials, find partners and potential users to provide feedback, prepare for open source, and set goals for the next 24 years

Reference

Enhance the adapter

Using the local backend in Pulumi to avoid logging in during deployment and destroying. But this is currently a simple and incomplete implementation. We might need to change the location where the state is saved, and generate the 'PASSPHRASE' dynamically.

  • Enhance the Adapter API so that it can save and load its state throughout the stack lifecycle.
  • Save the IaC engine state within the project scope, such as '.pulumi'.

Call a function from a lambda function

Currently, the Pluto deducer wraps each function defined in the arguments of the cloud resource handler function and treats it as a lambda resource. However, if a user tries to call another function, it will result in failure. This restriction hinders code reuse through functions.

import { CloudEvent, Queue } from "@plutolang/pluto";

const queue = new Queue("queue");

// This will not be included in the compilation result.
function anotherFunc() {
  console.log("Another");
}

queue.subscribe(async (evt: CloudEvent) => {
  // This is one lambda function.
  anotherFunc();
});

The code provided above is not functioning correctly. To address this issue, we need to incorporate function dependency analysis into the deduction process. Furthermore, we should record this dependency information in the architecture reference, such as the resource's location. Subsequently, when generating the compute module code, we will merge all the required functions together.

The other dependencies, such as classes, interfaces, and types, can be treated the same way.

Pluto 2024 Roadmap

Pluto 2024

Annual Goal: Increase Pluto's Visibility

  1. Individual Developer Outreach: Bring Pluto to individual developers' attention and make it a preferred option for cloud development.
  2. Acquiring Customers through Key Accounts: Enable large clients to integrate with their platforms, using Pluto to offer their platform capabilities to developers.

Goals for the First Half of the Year

Purpose:

  1. Make Pluto an option for individual developers for cloud development.
  2. Enable large clients to start integrating their platforms with Pluto. 

Vision:

  • Enhance Business Development Experience (Usability):
    • Friendly error prompts, including programming constraints and execution errors.
    • Developers can use familiar frameworks (Express, Koa, etc.) to harness Pluto's capabilities.
    • Developers can perform general computations (Word Count) in addition to web backend development with Pluto.
    • Accessible tutorials and case documentation for reference.
  • Simplify the Extension Development Process (Simplicity):
    • Users extend the cloud platform or modify resource types by updating four files: Client implementation class, Client base class, Infra implementation class, Infra base class.
    • Users can adapt platforms not supporting IaC by implementing third-party Adapters. 

Goals:

  • Product Feedback
    • Stars: 200+
    • Suggestions and Requirements: 10+
    • NPM Downloads: 3k+
  • Error Prompt Optimization
    • Build a comprehensive framework for error prompts.
    • Standardize error messages.
  • Support Framework Adaptation
    • Framework SDK Development: Express
    • Deep analysis of SDK capabilities.
    • Multi-file analysis capability.
  • Support General Computation Scenarios
    • Transition to main closure execution mode.
    • Passage of compile-time generated values #102.
  • Documentation Construction
    • Develop a documentation website.
    • Tutorials and a Playground.
    • Enrich case studies: 4+.
  • Simplify SDK Development
    • Adjust the timing of FaaS resource instance creation #103.
    • Change the timing of specific implementation class selection #106.
    • Auxiliary tools for Pluto NPM package release.
  • Build a working mechanism for non-IaC engine Adapters, supporting platforms like Simulation, HUSE, etc. 

Challenges:

  1. Implementation of static analysis.
  2. The adapter without IaC engine working mechanism design.

Goals for the Second Half of the Year

Purpose:

  1. Make Pluto a preferred option for individual developers for cloud development.
  2. Increase Pluto's visibility. 

Vision:

  • Enhance Business Development Experience (User-friendly):
    • Developers can debug code without using the cloud platform's console.
    • Developers can globally configure expected infrastructure through plugins, such as cost control, VPC settings, etc.
    • Developers can view resource consumption and cost statistics through the server. 

Goals:

  • Product Visibility:
    • Stars 500+
    • Suggestions and Requirements 30+
    • Contributors 10+
    • NPM Downloads 10k+
  • Enrich Pluto's Case Studies: 10+
  • Produce a Killer Application
    • Software delivery tools.
  • App Store (Challenge)
  • Design plugin mechanism and complete PoC verification.
  • Build server-side system framework.

An example of big data: word count

import { MD5 } from 'crypto-js';
import { Bucket, Function } from "@plutolang/pluto";

const inputBucket = new Bucket("articles", { 
  exists: true // Don't try to create this bucket when deploying this application.
});
const intermediateBucket = new Bucket("intermediates");
const outputBucket = new Bucket("words", { 
  destroy: false // Don't destroy this bucket when destroying this application.
});

const mapperNum = 10;
const reducerNum = 5;

async function mapFn(mapperId: number) {
  const intermediateOutputs: { word: string, count: number }[][] = [];
  for (let i = 0; i < reducerNum; i++) {
    intermediateOutputs.push([]);
  }

  const articleNum = await inputBucket.list();
  for (let i = mapperId; i < articleNum; i += mapperId) {
    let article = await inputBucket.get(`articles/${i}`);
    processOneArticle(article);
  }

  for (let i = 0; i < reducerNum; i++) {
    await intermediateBucket.set(`intermediates/${i}/${mapperId}`, JSON.stringify(intermediateOutputs[i]));
  }

  function processOneArticle(article: string) {
    // Replace all characters that aren't a letter or a hyphen with a space
    let processedArticle = article.replace(/[^a-zA-Z0-9\-]/g, ' ');

    // Collapse all whitespace
    processedArticle = processedArticle.replace(/s+/g, ' ');

    // Split the line by space and iterate over each word
    processedArticle.split(' ').forEach(function (word) {
      const hash = MD5(word).toString();
      const partition = parseInt(hash.substring(0, 8), 16) % reducerNum;
      // Emit the word and a 1
      intermediateOutputs[partition].push({ word: word, count: 1 });
    });
  }
}

async function reduceFn(reducerId: number) {
  // Read the intermediate outputs from each mapper and count the number of times each word appears.
  // The word counts are stored in a dictionary, where the key is the word and the value is the count.
  const wordCounts: { [word: string]: number } = {};
  for (let mapperId = 0; mapperId < mapperNum; mapperId++) {
    const intermediateOutput = JSON.parse(await intermediateBucket.get(`intermediates/${reducerId}/${mapperId}`));
    intermediateOutput.forEach(function (wordCount: { word: string, count: number }) {
      wordCounts[wordCount.word] = (wordCounts[wordCount.word] ?? 0) + wordCount.count;
    })
  }

  // Convert the word counts to an array of objects, and save them to output bucket.
  // The output bucket will be a list of objects, each object contains a word and its count.
  const outputs: { word: string, count: number }[] = [];
  for (const word in wordCounts) {
    outputs.push({ word: word, count: wordCounts[word] });
  }
  await outputBucket.set(`words/${reducerId}`, JSON.stringify(outputs));
}

// Sort the word counts by count and take the top k words
async function topK(k: number) {
  const words: { word: string, count: number }[] = [];
  for (let reducerId = 0; reducerId < reducerNum; reducerId++) {
    const output = JSON.parse(await outputBucket.get(`words/${reducerId}`));
    words.push(...output);
  }
  const sortedWords = words.sort((a, b) => b.count - a.count);
  return sortedWords.slice(0, k);
}

async function main() {
  // Create a list of mappers, and invoke them in parallel
  const mappers = []
  const mapFnResource = new Function(mapFn); // Should be OK to create a resource object here.
  for (let i = 0; i < mapperNum; i++) {
    const mapperId = i;  // Deep copy
    mappers.push(mapFnResource.invoke(mapperId));
    
    // Invalid case.
    // Create the resource object using the data generated at runtime.
    // const fn = new Function(async () => { await mapFn(mapperId); }); 
    // mapper.push(fn.invoke());
  }
  // Wait for all mappers to finish
  await Promise.all(mappers);

  // Create a list of reducers, and invoke them in parallel
  const reducers = [];
  const reduceFnResource = new Function(reduceFn);
  for (let i = 0; i < reducerNum; i++) {
    const reducerId = i; // Deep copy
    reducers.push(reduceFnResource.invoke(reducerId));
  }
  // Wait for all reducers to finish
  await Promise.all(reducers);

  // List the top 10 words based on their frequency in the result
  const topKFnResource = new Function(topK);
  const topKWords = await topKFnResource.invoke(10);
  console.log(topKWords);
}

main();

Expected Behavior

During compilation, deduce from the code that the entire application contains 4 lambda and 2 bucket resource instances, and generate a pure IaC (Infrastructure as Code) code to create these resource instances. Among them, the 4 lambda resource instances correspond to four sections of code: mapper, reducer, topk, and the main code. The main code excludes Infra API-related code.

After deployment, the lambda resource instance corresponding to the main code is automatically triggered to execute, and the logs of the 4 lambda resource instances are output in chronological order. The output ends when the execution process of this resource instance is completed.

During execution in the cloud, the various resource objects that are created and invoked are Client class objects of the resource types. For example, the return values of new Function and new Bucket in the code are client class instances of Function and Bucket, respectively.

Additional Information

If, aside from the scope that includes Infra API statements, all other code does not involve Client API calls, it indicates that the main code, apart from the Infra API's Handler and resource instances, is unrelated to cloud resources and can be executed locally.

Divide the argument type captured during deduction into more specific types

Describe the feature

There are two types of arguments captured during deduction: text and closure. These are stored in the architecture reference. The closure type represents a function object, with the captured value being the path of the bundle of this function. The text type represents a string that can be used to generate the IaC code directly. However, it is unclear which type of argument is captured during deduction. It could be a literal value, a jsonified value, an expression to access the captured resource property, or an expression to access an environment variable. Viewing and working with the architecture reference directly is not user-friendly.

Use Case

The simulator adapter utilizes the architecture reference to deploy the application directly on the local environment. Currently, there is no efficient method to distinguish the type of argument during deployment.

Proposed Solution

No response

Other Information

No response

Acknowledgements

  • I may be able to implement this feature request
  • This feature might incur a breaking change

Support interactive with the system

In the application, there may be a need to access data from external sources, such as retrieving environment variables or reading local files.

If the data is only going to be read and not written, we should upload it. Otherwise, we need to establish a mechanism that enables storing and sharing data among lambda functions. Alternatively, we can simply inform the user not to perform these actions.

Support multiple files

Currently, Pluto only supports a single file src/index.ts. If other files are imported, they will not be compiled and included in the generated code.

We need to consider what the structure of the generated files should be. Should each compute module have its own file or multiple files?

Enhance `log` command

Describe the feature

  • Add --filter option
  • After binding the Pluto resource with the Pulumi resource, optimize the function name displayed in the terminal

Use Case

In situations where you only need to access a particular function.

Proposed Solution

No response

Other Information

No response

Acknowledgements

  • I may be able to implement this feature request
  • This feature might incur a breaking change

Support accessing renamed resource variables

Currently, Pluto does not support accessing resource variables that have been renamed.

import { Router, HttpRequest, HttpResponse, Queue } from "@plutolang/pluto";

const router = new Router("router");

const queue = new Queue("queue");
const aliasQueue = queue;  // Will not appear in the generated computing module code

router.get("/store", async (req: HttpRequest): Promise<HttpResponse> => {
  await queue.push("foo");  // Valid
  await aliasQueue.push("bar");  // Invalid
  return {
    statusCode: 200,
    body: `Fetch access message.`,
  };
});

Unable to create client when only regular API is accessed

If the closure only accesses a regular API (not a client API, infrastructure API, or captured property), the deducer does not add the resource object to the closure's dependencies. Consequently, it will not create the client for the resource object within the closure code.

Importing langchain takes too long

The example "langchain-llama2-chatbot-sagemaker-python" creates a llama2 conversational chatbot using AWS and LangChain. When deploying it on AWS, the initial execution might time out. The issue we've identified is that importing LangChain is taking too long, about 50 seconds, even with .pyc files available. However, after the initial import, subsequent executions run smoothly.

We need to find why the initial import is taking so long and fix it.

Validate the cron expression and convert it to the required format

The schedule resource utilizes the cron expression to define the schedule. However, different runtimes have varying required formats. Hence, it is necessary for us to offer a standardized format for users. In the infra SDK, we need to validate whether the expression provided by the user is valid and convert it into the required format for that specific runtime.

Additionally, we need to consider the difference between the user's local time zone and the runtime. It would be beneficial for users if we could convert it to their preferred settings.

More capabilities for production.

There may be some capabilities that need to be supported for production usage.

  • Debugging capability. Allow developers to identify and troubleshoot issues that occur online, guiding them towards resolution.
  • Observability. Provide a basic dashboard that displays the usage and load of resources.
  • Cost control. Provide developers with several ways to reduce their expenses.

Cannot generate the resource client building statement correctly

Bug Report

Please answer these questions before submitting your issue. Thanks!

1. Minimal reproduce step (Required)

For the code below, the queue is a cloud resource. If a lambda function accesses the queue, in the generated compute module code, there should be a client building statement for the queue. However, currently, the generated statement is incorrect. It does not use 'def.Queue' as its initialized class but instead uses 'Queue'.

// User code
import * as def from "@plutolang/pluto";
const queue = new def.Queue();
// Generated compute module
import * as def from "@plutolang/pluto";
const queue = Queue.buildClient(...);  // Wrong!
const queue = def.Queue.buildClient(...);  // Expected

2. What did you expect to see? (Required)

Create the client by utilizing the initialized class that was used by the user.

3. What did you see instead (Required)

Create the client using a hard-coded approach.

4. What is your Pluto components version? (Required)

0.0.5

Enhance the exit mechanism of the pluto cli

Describe the feature

Currently, the pluto cli command is designed to require two presses of Ctrl-C to exit, as a measure to prevent accidental exits. However, the intended functionality is to pass the SIGINT signal to the simulator in order to initiate a graceful cleanup of resources. The first press of Ctrl-C is meant to be ignored by the cli and subsequently passed to the simulator. Upon receiving the signal, the simulator should perform a graceful cleanup. The second press of Ctrl-C should then be caught by the cli to exit the command line interface.

So, the current behavior is such that after the first press of Ctrl-C, the simulator exits. This is not the intuitive behavior that we expect.

What we expect are two possible behaviors:

  • Upon pressing Ctrl-C once, the simulator and cli should gracefully exit.
  • Alternatively, two presses of Ctrl-C should be required for the simulator to exit, ensuring that the application remains operational after the first press.

Use Case

pluto run

Proposed Solution

No response

Other Information

No response

Acknowledgements

  • I may be able to implement this feature request
  • This feature might incur a breaking change

Cannot work with 'then' expression

Bug Report

Please answer these questions before submitting your issue. Thanks!

1. Minimal reproduce step (Required)

const data = await kvstore.get(key).then( e => {});

Nested blocks do not take effect, including catch.

try {
  const data = await kvstore.get(key);
} catch (e) {
  // ....
}

2. What did you expect to see? (Required)

Get the correct relationship between lambda and kvstore.

3. What did you see instead (Required)

Didn't detect the relationship between lambda and kvstore.

4. What is your Pluto components version? (Required)

An example of web

import { Router, HttpRequest, HttpResponse } from "@plutolang/pluto";

var users = [
  { name: 'tobi' },
  { name: 'loki' },
  { name: 'jane' }
];

const router = new Router("router");

router.get("/api/users", async (req: HttpRequest): Promise<HttpResponse> => {
  return { statusCode: 200, body: JSON.stringify(users) };
});

router.post("/api/user/:name", async (req: HttpRequest): Promise<HttpResponse> => {
  const newUser = req.params.name;
  users.push({ name: newUser });
  return { statusCode: 200 };
});

// Can be used to display the URL of the HTTP server.
console.log(`The URL of the HTTP server is`, router.url);


const routerTester = new Tester("router tester");

routerTester.it("test case", async () => {
  // Can be used to perform end-to-end testing for the HTTP server.
  const resp = await fetch(router.url);
  if (resp.status !== 200) {
    throw new Error("failed.");
  }
});

Expected Behavior

During the testing phase, deduce and generate 3 lambda functions corresponding to two handlers of the Router and the test case of the Tester. After deployment, invoke the lambda corresponding to the Tester's test case for execution.

In the deployment phase, also deduce and generate 3 lambda functions corresponding to the two handlers of the Router and the main code. After deployment, invoke the lambda corresponding to the main code for execution.

Support unit testing feature

Enable the unit testing feature, allowing users to select either a simulator or real runtime as their testing environment. Testing in the real runtime helps prevent issues where code functions correctly locally but fails in the online environment. Testing in the simulator is faster and allows for quick identification of problems.

The current idea is to provide users with a user interface similar to vitest. During testing, each unit test will be given an independent environment to avoid interference between tests. At the same time, only the resources related to each unit test will be deployed, in order to avoid resource waste and save time.

To support testing, add a resource called "Tester". The implementation for Tester will output the ID of all function resources. This ID will be used to invoke the test function.

import { Tester } from "@plutolang/pluto";
import assert from "assert";

const tester = new Tester("function call");

function add(a: number, b: number): number {
  return a + b;
}

tester.it("add case1", async () => {
  assert(add(1, 3) == 3);
});

test.it("add case2", async () => {
  assert(add(1, 3) == 4);
});

Deducer will analyze the code as usual. The CLI will divide the architecture reference into multiple groups based on testers. Each group represents a new architecture reference and includes only one tester along with the necessary resources for that tester. Each group will be tested separately.

The CLI will deploy each architecture reference, retrieve the function resource ID from the adapter's outputs, and create a TesterClient to invoke each test function.

  • Split the architecture reference into multiple groups based on testers.
  • Move the TesterClient into the SDK.

KVStore set method fails to work on AWS

Bug Report

Please answer these questions before submitting your issue. Thanks!

1. Minimal reproduce step (Required)

When running this code as Lambda entry point, the "Start sending" log is printed successfully. However, there are no additional logs, neither "Response" nor "Error".

exports.default = async (event, ctx) => {
    event["Records"].forEach(async (record) => {
      if (!("Sns" in record)) {
        throw new Error(`Unsupported event type ${JSON.stringify(record)}`);
      }

      const payload = record["Sns"]["Message"];
      console.log(record, payload, typeof payload);
      const event = JSON.parse(payload);
      console.log(event);
      
      
      const data = JSON.parse(event.data);
      const key = data["name"];
      const val = data["message"];
      
      console.log("Key: ", key, "| Type: ", typeof key);
      console.log("Value: ", val, "| Type: ", typeof val);
      
      try {
        // const client = new DynamoDBClient();
        // const docClient = DynamoDBDocumentClient.from(client);
        const command = new PutCommand({
          TableName: "kvstore",
          Item: {
            Id: key,
            Value: val,
          },
        });
        console.log("Start sending");
        const response = await docClient.send(command);
        console.log("Response: ", response);
      } catch (e) {
        console.log("Error: ", e);
      }
    });
}

2. What did you expect to see? (Required)

persist the key-value pair in DynamoDB and print the response.

3. What did you see instead (Required)

No "response" nor "error"

4. What is your Pluto components version? (Required)

Support more components of AliCloud

After merging pull request #81, we have achieved basic support for AliCloud, including ApiGateway as a router and FC as a FnResource. However, we acknowledge that there is still a need for support for more components of AliCloud. We are actively working on expanding our SDK to include additional functionalities and components from AliCloud, in order to provide a comprehensive solution for developers.

  • KVStore
  • Queue
  • Schedule

Speed up dependency extraction for Python projects.

Currently, the slow deduction speed is mainly due to the slow dependency extraction, which presents several issues:

  1. Whenever there's a change in one part of the code, almost all business sections need to reinstall dependencies.
  2. Even if dependencies remain unchanged, any code modification requires reinstallation of dependencies.
  3. Repeated dependencies between different closures need reinstallation, even when using pip cache, the installation speed remains slow.

Instantiate multiple instances of resource clients when a closure repeatedly accesses the same resource object

In the current deduction system, multiple accesses to the same resource object within a closure do not result in reduced dependencies. This leads to attempts at creating several clients for the identical resource object, resulting in more than one variable with the same name.

Take this as an example: we call both invoke and endpointUrl methods of a single sagemaker object within one router handler. The deducer identifies two dependencies related to this sagemaker object for the router handler's corresponding compute closure. One is classified as a client API dependency while the other is seen as a captured property dependency. When extracting this closure into a separate directory, it's necessary for our deducer to create a sagemaker client. Ideally, only one such client should be created; however, currently two identical ones are being generated instead. This causes exceptions during runtime on cloud platforms.

router.post("/generate", async (req) => {
  console.log("the endpoint url of the sagemaker model is", sagemaker.endpointUrl());

  const payload = req.body;
  if (!payload) {
    return {
      statusCode: 400,
      body: "The request body is empty. Please provide a valid input.",
    };
  }

  const data = JSON.parse(payload);
  if (!data["inputs"]) {
    // The payload should be a JSON object with a key "inputs".
    return {
      statusCode: 400,
      body: "The request body is invalid. Please provide a valid input.",
    };
  }

  // Invoke the SageMaker endpoint with the input data and return the response to the users.
  const output = await sagemaker.invoke(data);
  return {
    statusCode: 200,
    body: JSON.stringify(output),
  };
});

Enhance CLI

We need to enhance the CLI, which includes:

  • Adding additional commands related to stacks.
    • Implementing "stack delete" command: This command will delete a specific stack.
    • Implementing "stack activate" command: This command will set a specific stack as the default option for other commands.
    • Implementing "stack list" command: This command will display a list of all stacks created in the current project.
  • Improving error messages to make them more informative and user-friendly.
  • Adding an exit message when the user forces an exit, such as by pressing Ctrl-C.

Move the selection of specific implementation classes to the static method within the base classes

During the Proof of Concept (PoC) phase, specific Client implementation classes were created through the resource type's buildClient method, while specific Infra implementation classes were registered in the Registry and then retrieved by querying the Registry based on platform and engine type. Both of these methods introduced additional cognitive load for SDK developers, requiring an understanding of the specific creation methods and adding extra logic to Pluto's implementation. Simplification in this area could be explored.

// Resource Client 
export class Queue implements Resource {
  constructor(name: string, opts?: QueueOptions) {
    name;
    opts;
    throw new Error(
      "Cannot instantiate this class, instead of its subclass depending on the target runtime."
    );
  }

  public static buildClient(name: string, opts?: QueueClientOptions): QueueClient {
    const rtType = process.env["RUNTIME_TYPE"];
    switch (rtType) {
      case runtime.Type.AWS:
        return new aws.SNSQueue(name, opts);
      case runtime.Type.K8s:
        return new k8s.RedisQueue(name, opts);
      case runtime.Type.Simulator:
        if (!process.env.PLUTO_SIMULATOR_URL) throw new Error("PLUTO_SIMULATOR_URL doesn't exist");
        return simulator.makeSimulatorClient(process.env.PLUTO_SIMULATOR_URL!, name);
      default:
        throw new Error(`not support this runtime '${rtType}'`);
    }
  }
}

// Resource Infra
export function register(reg: Registry) {
  reg.register(runtime.Type.AWS, engine.Type.pulumi, Router, aws.ApiGatewayRouter);
  reg.register(runtime.Type.AWS, engine.Type.pulumi, KVStore, aws.DynamoKVStore);
  reg.register(runtime.Type.AWS, engine.Type.pulumi, Queue, aws.SNSQueue);
  reg.register(runtime.Type.AWS, engine.Type.pulumi, Schedule, aws.CloudWatchSchedule);
  reg.register(runtime.Type.AWS, engine.Type.pulumi, "FnResource", aws.Lambda);
  reg.register(runtime.Type.AWS, engine.Type.pulumi, Tester, aws.Tester);
}

We can place the logic for selecting the specific implementation class within the constructor, allowing SDK developers to manage this process. By eliminating Registry and buildClient, Pluto would no longer have to add extra logic for handling type conversion and could instead focus solely on maintaining user code.

We'd like to instantiate the implementation classes asynchronously, which can help cut down on package loading costs. This process will be encapsulated within an asynchronous function, often referred to as a 'lazy importing' function. However, in JavaScript, it's not feasible to call an async function within a constructor.

So we've come up with a solution where each resource has an abstract base class at the root of the infra SDK and includes a static async method named createInstance. This method serves to create instances of resource infrastructure implementation classes based on the target platform and engine.

Not only does this strategy streamline operations, but it also allows SDK developers to concentrate more effectively on their tasks.

The stipulation is that the parameters for both the client implementation class and infrastructure implementation class of a resource, along with those of the createInstance method from its base class, must all be consistent.

abstract class Queue {
    public static async createInstance(name, opts) {
      if (currentPlatform == "AWS") {
          return new SNSQueue(name, opts);
        } else if (...) {
           // ...
        }
    }
}

class SNSQueue {
    public subscribe(handler: any) {
        // do something...
    }
}

Enhance function serialization

User-written computational closures are serialized during the deduction stage. At deployment, platform adaptation code is added around the user's closure and then packaged for upload to the platform. This process is included in the Infrastructure SDK.

We hope that developers of SDKs write their adaptation code within the same file as other infrastructure codes. During execution, Pluto leverages Node.js runtime capabilities to dynamically package this adaptation code with user closures, rather than writing them separately in two files and combining them using string operations.

Currently, we utilize Pulumi runtime's serializeFunction capability for implementation but there's a drawback: it requires SDK developers to name corresponding user closure parameters as __handler_ when developing platform-adapting functions. This is because Pulumi’s serialization ability has certain limitations; hence we avoid using it for serializing user-written computational closures. Instead, we replace parts of users' closures with require in Pulumi’s serialized results. Therefore, a uniform variable needs to be agreed upon as a placeholder guiding us on which part of the code should be replaced.

Using capabilities provided by Pulumi not only restricts how SDK developers write their codes but also leads to an issue where one Infra API can only support one computational closure due to potential naming conflicts.

A possible solution: We could develop our own function similar to Pulumi's serialization capability where during serialization if variables being serialized are closures and if these have non-empty 'dirpath', they would directly require without further deep serialization.

Building Runtime Handler interface specifications to reduce the burden of platform and resource integration

Users interact with resources through a unified interface, regardless of the specific cloud platform. However, when deploying user-written business logic to a cloud platform, adaptation is required. Payload obtained from the platform runtime needs to be converted into parameters that the business logic code can accept. Furthermore, the execution results of the business logic must be adapted to the platform's standards for response.

request → biz_input → biz_function → biz_output → response

The current implementation method is that each type of resource directly faces the platform's standards and directly adapts to the platform's standards. With n types of resources and m platforms, the cost of adaptation is n * m.

We can define a set of Runtime Handler interface specifications between the platform and the resource types. The input of the interface is the platform input, and the output is the business logic result. Each type of resource only needs to implement one Runtime Handler to adapt the business logic. Each platform only needs to implement one Runtime adaptation function, converting the platform's input into the input type of the Runtime Handler, and calling the Runtime Handler. Finally, the output of the Runtime Handler is converted into the platform output. The complexity is reduced to n + m.

In this way, to integrate a platform, you only need to create a file, adapt the platform's input and output, and call the Runtime Handler, and all resources can be deployed to this platform. Similarly, to integrate a new type of resource, you only need to create a file, adapt the business logic interface and the Runtime Handler interface, and it can be deployed to various platforms. This reduces the cost of integrating resources and platforms.

infra/
├── handlers  # runtime handlers
│   ├── python
│   │   ├── function.py
│   │   ├── queue.py
│   │   └── router.py
│   └── typescript
│       ├── function.ts
│       ├── queue.ts
│       └── router.ts
├── aws
│   ├── adapters # runtime adapters for AWS
│   │   ├── runtime_adapter.py
│   │   └── runtime_adapter.ts
│   ├── router.apigateway.ts
│   └── sns.queue.ts
└── k8s
    ├── adapters # runtime adapters for Kubernetes
    │   ├── runtime_adapter.py
    │   └── runtime_adapter.ts
    ├── queue.redis.ts
    └── router.ingress.ts

Support WebSocket protocol

The text generation process of large language models usually takes some time, so it's common to use streams to send the result back to users. Therefore, we need to support the WebSocket protocol in both the Router and Function.

Allow users to add custom permissions

Feature Request

Is your feature request related to a problem? Please describe:

Developers may utilize the AWS SDK in their code to handle their AWS resources. They need to grant additional permissions to the lambda IAM for this purpose. Otherwise, the lambda will be unable to access the resources that developers intend to manage.

For instance, in Pluto's AWS authentication service, the second lambda must execute the sts::AssumeRole command in order to obtain credentials for the target role. Without manual intervention, an error will occur:

User: arn:aws:sts::811762874732:assumed-role/lambda2-iam-db3a970/lambda2-lambda-c60e627 is not authorized to perform: sts:AssumeRole on resource: arn:aws:iam::811762874732:role/PLRole-xxx

Describe the feature you'd like:

Allow users to add the necessary custom permissions.

Describe alternatives you've considered:

Maybe we can take inspiration from Nitric's implementation approach:

router.get("/query", async (req: HttpRequest): Promise<HttpResponse> => {
  this.getPermission("sts:AssumeRole", resources);
  // ...
}

Teachability, Documentation, Adoption, Migration Strategy:

Access an external variable from a lambda function

At present, the Pluto deducer lacks the capability to analyze and track the locations of accessed variables. Consequently, when a lambda function accesses a variable beyond its own scope, said variable will not be incorporated into the lambda.

import { HttpRequest, HttpResponse, Router } from "@plutolang/pluto";

// This will not be included in the generated compute module code.
const CONSTANT = "constant";
let variable = 1;

const router = new Router("router");

router.get("/path1", async (req: HttpRequest): Promise<HttpResponse> => {
  console.log(CONSTANT); // failed
  variable += 1;
  // ...
});

router.get("/path2", async (req: HttpRequest): Promise<HttpResponse> => {
  console.log(variable); // failed
  // ...
});

Our expectation is that the constant will be treated as a regular constant and included in the generated code. However, the variable may need to be transferred to a cloud resource, such as an object in an object store, in order to preserve its state information. This cloud resource for the variable needs to be present in the architecture reference.

  • Support for constants access.
  • Support for variable variables access.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.