Skip to content

Commit

Permalink
fix: Simplify the quickstart (#247)
Browse files Browse the repository at this point in the history
* fix: Simplify the quickstart

* update

* update

* update

* update
  • Loading branch information
nadeesha authored Dec 9, 2024
1 parent 5c648d1 commit 4714249
Show file tree
Hide file tree
Showing 18 changed files with 475 additions and 983 deletions.
76 changes: 76 additions & 0 deletions bootstrap-dotnet/ExecService.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Threading.Tasks;

public static class ExecService
{
private static readonly HashSet<string> AllowedCommands = new HashSet<string>
{
"ls",
"cat"
};

public static ExecResponse Exec(ExecInput input)
{
try
{
if (!AllowedCommands.Contains(input.Command))
{
return new ExecResponse
{
Error = $"Command '{input.Command}' is not allowed. Only 'ls' and 'cat' are permitted."
};
}

if (!input.Arg.StartsWith("./"))
{
return new ExecResponse
{
Error = "Can only access paths starting with ./"
};
}

var startInfo = new ProcessStartInfo
{
FileName = input.Command,
Arguments = input.Arg,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
};

using var process = Process.Start(startInfo);
var stdout = process?.StandardOutput.ReadToEnd().Trim();
var stderr = process?.StandardError.ReadToEnd().Trim();
process?.WaitForExit();

return new ExecResponse
{
Stdout = stdout,
Stderr = stderr
};
}
catch (Exception ex)
{
return new ExecResponse
{
Error = ex.Message
};
}
}
}

public struct ExecInput
{
public required string Command { get; set; }
public required string Arg { get; set; }
}

public class ExecResponse
{
public string? Stdout { get; set; }
public string? Stderr { get; set; }
public string? Error { get; set; }
}
73 changes: 0 additions & 73 deletions bootstrap-dotnet/HackerNewsService.cs

This file was deleted.

27 changes: 11 additions & 16 deletions bootstrap-dotnet/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,17 @@ class Program
{
static async Task Main(string[] args)
{
// Load environment variables in development
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Development")
{
Env.Load();
}

// Setup dependency injection
var services = new ServiceCollection();

// Add logging
services.AddLogging(builder => {
builder.AddConsole();
});

// Configure Inferable client
services.AddSingleton<InferableClient>(sp => {
var options = new InferableOptions {
ApiSecret = Environment.GetEnvironmentVariable("INFERABLE_API_SECRET"),
Expand All @@ -35,21 +31,20 @@ static async Task Main(string[] args)
var serviceProvider = services.BuildServiceProvider();
var client = serviceProvider.GetRequiredService<InferableClient>();

// Check command line arguments
if (args.Length > 0 && args[0].ToLower() == "run")
// Check if "trigger" command was passed
if (args.Length > 0 && args[0].ToLower() == "trigger")
{
Console.WriteLine("Running HN extraction...");
await RunHNExtraction.RunAsync(client);
await RunSourceInspection.RunAsync(client);
return;
}
else
{
Console.WriteLine("Starting client...");
Register.RegisterFunctions(client);
await client.Default.StartAsync();

Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
// Default behavior - run the service
Console.WriteLine("Starting client...");
Register.RegisterFunctions(client);
await client.Default.StartAsync();

Console.WriteLine("Press any key to exit...");
Console.ReadKey();
}
}

Expand Down
122 changes: 82 additions & 40 deletions bootstrap-dotnet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,67 +2,109 @@
<img src="https://a.inferable.ai/logo-hex.png" width="200" style="border-radius: 10px" />
</p>

# Inferable .NET Bootstrap
# .NET Bootstrap for Inferable

This is a .NET bootstrap application that demonstrates how to integrate and use our SDK. It serves as a reference implementation and starting point for .NET developers.
[![NuGet version](https://img.shields.io/nuget/v/Inferable.svg)](https://www.nuget.org/packages/Inferable/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
[![Documentation](https://img.shields.io/badge/docs-inferable.ai-brightgreen)](https://docs.inferable.ai/)

## The Application
This is a bootstrap project demonstrating how to create an Inferable service in .NET.

The application is a simple .NET application that extracts the top posts from Hacker News and summarizes the comments for each post. It demonstrates how to:
## Installation

- Register C# functions with Inferable
- Trigger a Run programmatically to orchestrate the functions
- Control the control flow of the Run using native C# async/await primitives
1. Clone this repository
2. Install the Inferable NuGet package:

```mermaid
sequenceDiagram
participant extract
participant summarizePost
participant generatePage
```bash
dotnet add package Inferable
```

## Quick Start

### Initializing the Client

```csharp
using Inferable;

extract->>extract: Get top 3 HN posts
extract->>summarizePost: Posts data
summarizePost->>summarizePost: Get & analyze comments
summarizePost->>generatePage: Summaries data
generatePage->>generatePage: Generate HTML
var options = new InferableOptions
{
ApiSecret = "your-api-secret", // Get yours at https://console.inferable.ai
BaseUrl = "https://api.inferable.ai" // Optional
};

var client = new InferableClient(options);
```

## How to Run
If you don't provide an API key or base URL, it will attempt to read them from the following environment variables:

1. Start the local worker machine:
- `INFERABLE_API_SECRET`
- `INFERABLE_API_ENDPOINT`

```bash
dotnet run
### Registering the Exec Function

This bootstrap demonstrates registering a secure command execution [function](https://docs.inferable.ai/pages/functions):

```csharp
public class ExecInput
{
[JsonPropertyName("command")]
public string Command { get; set; } // Only "ls" or "cat" allowed
[JsonPropertyName("arg")]
public string Arg { get; set; } // Must start with "./"
}

client.Default.RegisterFunction(new FunctionRegistration<ExecInput> {
Name = "exec",
Description = "Executes a system command (only 'ls' and 'cat' are allowed)",
Func = new Func<ExecInput, object?>(ExecService.Exec),
});

await client.Default.StartAsync();
```

2. Trigger the HN extraction:
### Using the Function

The exec function can be called through Inferable to execute safe system commands:

```bash
dotnet run run
ls ./src # Lists contents of ./src directory
cat ./README.md # Shows contents of README.md
```

## How it works
The function returns:

```csharp
public class ExecResponse
{
public string Stdout { get; set; } // Command output
public string Stderr { get; set; } // Error output if any
public string Error { get; set; } // Exception message if failed
}
```

### Security Features

This bootstrap implements several security measures:

- Whitelist of allowed commands (`ls` and `cat` only)
- Path validation (only allows paths starting with `./`)
- Safe process execution settings
- Full error capture and reporting

1. The worker machine uses the Inferable .NET SDK to register functions with Inferable. These functions are registered in the `Register.cs` file:
## Documentation

- `GetUrlContent`: Fetches the content of a URL and strips HTML tags
- `ScoreHNPost`: Scores a Hacker News post based on upvotes and comment count
- `GeneratePage`: Generates an HTML page from markdown
- [Inferable Documentation](https://docs.inferable.ai/)
- [.NET SDK Documentation](https://docs.inferable.ai/dotnet)

2. The `Run.cs` script defines three main operations that are orchestrated by Inferable:
## Support

- `extract`: Extracts and scores the top 10 HN posts, selecting the top 3
- `summarizePost`: Summarizes the comments for each selected post
- `generatePage`: Generates an HTML page containing the summaries
For support or questions, please [create an issue in the repository](https://github.com/inferablehq/inferable/issues).

3. The application flow:
## Contributing

- The extract operation fetches the top posts from Hacker News and scores them using internal scoring logic
- For each selected post, a separate summarization run analyzes the comments
- Finally, the generate operation creates an HTML page containing all the summaries
Contributions to the Inferable .NET Bootstrap are welcome. Please ensure that your code adheres to the existing style and includes appropriate tests.

4. The application uses C# primitives for control flow:
## License

- Async/await for non-blocking operations
- Strong typing with C# records and classes
- Reflection for inferring the function signatures and result schema
MIT
Loading

0 comments on commit 4714249

Please sign in to comment.