Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ You can also use local models, e.g., via [Ollama](https://ollama.com/). Download

Select a cell and type `=PROMPT("What model are you and who made you?")`. For Gemma 3 4B, it will tell you that it's called "Gemma" and made by Google DeepMind.

You can also use cell references. For example, copy a news article into cell A1 and type in cell B1: `=PROMPT("Extract all person names mentioned in the text", A1)`. You can reference many cells using standard Excel notation, e.g. `=PROMPT("Extract all person names in the cells", A1:F10)`
You can also use cell references. For example, copy a news article into cell A1 and type in cell B1: `=PROMPT("Extract all person names mentioned in the text", A1)`. You can reference many cells using standard Excel notation, e.g. `=PROMPT("Extract all person names in the cells", A1:F10)` or reference multiple separate ranges, e.g. `=PROMPT("Compare these datasets", A1:B10, D1:E10)`

For more advanced usage, including function calling and configuration, see our [documentation](https://docs.getcellm.com).

Expand Down Expand Up @@ -99,7 +99,7 @@ We think Cellm is really cool because it enables everyone to automate tasks with
To help us improve Cellm, we collect limited, anonymous telemetry data:

- **Crash reports:** To help us fix bugs.
- **Prompts:** To help us understand usage patterns. For example, if you use `=PROMPT(A1:B2, "Extract person names")`, we capture the text "Extract person names" and prompt options. The prompt options are things like the model you use and the temperature setting. We do not capture the data in cells A1:B2.
- **Prompts:** To help us understand usage patterns. For example, if you use `=PROMPT("Extract person names", A1:B2)`, we capture the text "Extract person names" and prompt options. The prompt options are things like the model you use and the temperature setting. We do not capture the data in cells A1:B2.

We do not collect any data from your spreadsheet and we have no way of associating your prompts with you. You can see for yourself at [src/Cellm/Models/Behaviors/SentryBehavior.cs](src/Cellm/Models/Behaviors/SentryBehavior.cs).

Expand Down
34 changes: 14 additions & 20 deletions docs/api-reference/functions/prompt-model.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -18,24 +18,10 @@ Allows you to call a model from a cell formula and specify the model as the firs
Can be a string, a cell reference, or a range of cells.
</ParamField>

<ParamField body="cellsOrTemperature" type="string | number">
Either context cells or a temperature value:
<ParamField body="cells" type="cell reference(s)">
One or more cell references or ranges as context for your prompt (e.g., A1, B2:C3, D4).

- **If providing cells:** A cell reference or range (e.g., A1:D10). The model will use these cells as context when following your instructions.
- **If providing a temperature:** A number (0.0-1.0) or preset string to control randomness when no context cells are needed.
</ParamField>

<ParamField body="temperature" type="string | number" default="0.0">
Controls randomness in the model's output.

Accepts either:
- A number between 0.0 and 1.0
- A preset string:
- `"Consistent"` (0.0) - Deterministic output, same result each time
- `"Neutral"` (0.3) - Balanced between consistency and variety
- `"Creative"` (0.7) - More varied and creative outputs

Lower values (closer to 0) produce consistent, deterministic responses. Higher values (closer to 1) produce more varied, creative responses.
You can provide multiple separate cell references that will all be included as context.
</ParamField>

## Returns
Expand All @@ -44,6 +30,10 @@ Allows you to call a model from a cell formula and specify the model as the firs
The model's response as plain text.
</ResponseField>

<Note>
Temperature is configured via the ribbon UI. Use the Temperature dropdown in the Model section to control randomness (Consistent/0.0, Neutral/0.3, Creative/0.7, or any value 0.0-1.0).
</Note>

<RequestExample>

```excel Text Instructions
Expand All @@ -58,8 +48,12 @@ Allows you to call a model from a cell formula and specify the model as the firs
=PROMPTMODEL("openai/gpt-4o-mini", "Extract keywords", A1:D10)
```

```excel With Temperature
=PROMPTMODEL("openai/gpt-4o-mini", "Extract keywords", A1:D10, 0.7)
```excel Multiple Cell Ranges
=PROMPTMODEL("openai/gpt-4o-mini", "Compare these datasets", A1:B10, D1:E10)
```

```excel Mixed Cell References
=PROMPTMODEL("openai/gpt-4o-mini", "Analyze all data", A1, B2:C5, D6)
```

</RequestExample>
Expand All @@ -78,4 +72,4 @@ Allows you to call a model from a cell formula and specify the model as the firs
=PROMPTMODEL.TORANGE("openai/gpt-4o-mini", "Extract keywords", A1:D10)
```

</ResponseExample>
</ResponseExample>
34 changes: 14 additions & 20 deletions docs/api-reference/functions/prompt.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -12,30 +12,20 @@ Allows you to call the default model from a cell formula.
Can be a string, a cell reference, or a range of cells.
</ParamField>

<ParamField body="cellsOrTemperature" type="string | number">
Either context cells or a temperature value:
<ParamField body="cells" type="cell reference(s)">
One or more cell references or ranges as context for your prompt (e.g., A1, B2:C3, D4).

- **If providing cells:** A cell reference or range (e.g., A1:D10). The model will use these cells as context when following your instructions.
- **If providing a temperature:** A number (0.0-1.0) or preset string to control randomness when no context cells are needed.
</ParamField>

<ParamField body="temperature" type="string | number" default="0.0">
Controls randomness in the model's output.

Accepts either:
- A number between 0.0 and 1.0
- A preset string:
- `"Consistent"` (0.0) - Deterministic output, same result each time
- `"Neutral"` (0.3) - Balanced between consistency and variety
- `"Creative"` (0.7) - More varied and creative outputs

Lower values (closer to 0) produce consistent, deterministic responses. Higher values (closer to 1) produce more varied, creative responses.
You can provide multiple separate cell references that will all be included as context.
</ParamField>

<ResponseField name="response" type="string">
The model's response as plain text.
</ResponseField>

<Note>
Temperature is configured via the ribbon UI. Use the Temperature dropdown in the Model section to control randomness (Consistent/0.0, Neutral/0.3, Creative/0.7, or any value 0.0-1.0).
</Note>

<RequestExample>

```excel Text Instructions
Expand All @@ -50,8 +40,12 @@ Allows you to call the default model from a cell formula.
=PROMPT("Extract keywords", A1:D10)
```

```excel With Temperature
=PROMPT("Extract keywords", A1:D10, 0.7)
```excel Multiple Cell Ranges
=PROMPT("Compare these datasets", A1:B10, D1:E10)
```

```excel Mixed Cell References
=PROMPT("Analyze all data", A1, B2:C5, D6)
```

</RequestExample>
Expand All @@ -70,4 +64,4 @@ Allows you to call the default model from a cell formula.
=PROMPT.TORANGE("Extract keywords", A1:D10)
```

</ResponseExample>
</ResponseExample>
6 changes: 6 additions & 0 deletions docs/get-started/quickstart.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,12 @@ You can reference a group of cells using standard Excel notation (this is called
=PROMPT("Extract all person names in the cells", A1:F10)
````

You can also reference multiple separate cell ranges:

````mdx Multiple ranges
=PROMPT("Compare the data in these two tables", A1:C10, E1:G10)
````

### Spilling model response across multiple cells
You can use the `=PROMPT.TOCOLUMN()` function to spill the model response across multiple cells. For example, if you have text in cell A1, you can type in cell B1:

Expand Down
101 changes: 62 additions & 39 deletions src/Cellm/AddIn/ArgumentParser.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,7 @@ public class ArgumentParser(IConfiguration configuration)
private object? _provider;
private object? _model;
private object? _instructions;
private object? _cellsOrTemperature;
private object? _temperature;
private object[]? _ranges;
private StructuredOutputShape _outputShape = StructuredOutputShape.None;

public static readonly string CellsBeginTag = "<cells>";
Expand Down Expand Up @@ -43,16 +42,9 @@ public ArgumentParser AddInstructions(object instructions)
return this;
}

public ArgumentParser AddCellsOrTemperature(object cellsOrTemperature)
public ArgumentParser AddCells(object[] ranges)
{
_cellsOrTemperature = cellsOrTemperature;

return this;
}

public ArgumentParser AddTemperature(object temperature)
{
_temperature = temperature;
_ranges = ranges;

return this;
}
Expand Down Expand Up @@ -82,51 +74,72 @@ internal Arguments Parse()
var defaultTemperature = configuration[$"{nameof(CellmAddInConfiguration)}:{nameof(CellmAddInConfiguration.DefaultTemperature)}"]
?? throw new ArgumentException(nameof(CellmAddInConfiguration.DefaultTemperature));

var arguments = (_instructions, _cellsOrTemperature, _temperature) switch
var temperature = ParseTemperature(defaultTemperature);

var arguments = (_instructions, _ranges) switch
{
// =PROMPT("Hello world")
(string instructions, ExcelMissing, ExcelMissing) => new Arguments(provider, model, string.Empty, instructions, ParseTemperature(defaultTemperature), _outputShape),
// =PROMPT("Hello world", 0.7)
(string instructions, double temperature, ExcelMissing) => new Arguments(provider, model, string.Empty, instructions, ParseTemperature(temperature), _outputShape),
// =PROMPT("Hello world", A1:B2)
(string instructions, ExcelReference cells, ExcelMissing) => new Arguments(provider, model, new Cells(cells.RowFirst, cells.ColumnFirst, cells.GetValue()), instructions, ParseTemperature(defaultTemperature), _outputShape),
// =PROMPT("Hello world", A1:B2, 0.7)
(string instructions, ExcelReference cells, double temperature) => new Arguments(provider, model, new Cells(cells.RowFirst, cells.ColumnFirst, cells.GetValue()), instructions, ParseTemperature(temperature), _outputShape),
(string instructions, []) => new Arguments(provider, model, [], instructions, temperature, _outputShape),
// =PROMPT("Hello world", A1, B2, ...)
(string instructions, object[] ranges) => new Arguments(provider, model, ParseRanges(ranges), instructions, temperature, _outputShape),
// =PROMPT(A1:B2)
(ExcelReference instructions, ExcelMissing, ExcelMissing) => new Arguments(provider, model, string.Empty, new Cells(instructions.RowFirst, instructions.ColumnFirst, instructions.GetValue()), ParseTemperature(defaultTemperature), _outputShape),
// =PROMPT(A1:B2, 0.7)
(ExcelReference instructions, double temperature, ExcelMissing) => new Arguments(provider, model, string.Empty, new Cells(instructions.RowFirst, instructions.ColumnFirst, instructions.GetValue()), ParseTemperature(temperature), _outputShape),
// =PROMPT(A1:B2, C1:D2)
(ExcelReference instructions, ExcelReference cells, ExcelMissing) => new Arguments(provider, model, new Cells(cells.RowFirst, cells.ColumnFirst, cells.GetValue()), new Cells(instructions.RowFirst, instructions.ColumnFirst, instructions.GetValue()), ParseTemperature(defaultTemperature), _outputShape),
// =PROMPT(A1:B2, C1:D2, 0.7)
(ExcelReference instructions, ExcelReference cells, double temperature) => new Arguments(provider, model, new Cells(cells.RowFirst, cells.ColumnFirst, cells.GetValue()), new Cells(instructions.RowFirst, instructions.ColumnFirst, instructions.GetValue()), ParseTemperature(temperature), _outputShape),
(ExcelReference instructions, []) => new Arguments(provider, model, [], new Range(instructions.RowFirst, instructions.ColumnFirst, instructions.GetValue()), temperature, _outputShape),
// =PROMPT(A1:B2, C1, D2, ...)
(ExcelReference instructions, object[] ranges) => new Arguments(provider, model, ParseRanges(ranges), new Range(instructions.RowFirst, instructions.ColumnFirst, instructions.GetValue()), temperature, _outputShape),
// Anything else
_ => throw new ArgumentException($"Invalid arguments ({_instructions?.GetType().Name}, {_cellsOrTemperature?.GetType().Name}, {_temperature?.GetType().Name})")
_ => throw new ArgumentException($"Invalid arguments ({_instructions?.GetType().Name}, {_ranges?.GetType().Name})")
};

if (arguments.Cells is Cells contextCells && contextCells.Values is ExcelError contextCellsError)
// Validate cells for errors
foreach (var range in arguments.Ranges)
{
throw new ExcelErrorException(contextCellsError);
if (range.Values is ExcelError rangeError)
{
throw new ExcelErrorException(rangeError);
}
}

if (arguments.Instructions is Cells instructionCells && instructionCells.Values is ExcelError instructionsCellsError)
if (arguments.Instructions is Range instructionCells && instructionCells.Values is ExcelError instructionsCellsError)
{
throw new ExcelErrorException(instructionsCellsError);
}

return arguments;
}

internal static string AddCells(string cells)
private static List<Range> ParseRanges(object[] ranges)
{
var result = new List<Range>();

foreach (var range in ranges)
{
switch (range)
{
case ExcelReference excelReference:
result.Add(new Range(excelReference.RowFirst, excelReference.ColumnFirst, excelReference.GetValue()));
break;
case ExcelMissing:
break;
case ExcelError error:
throw new ExcelErrorException(error);
default:
throw new ArgumentException($"Expected cell reference, got {range?.GetType().Name}");
}
}

return result;
}

internal static string FormatRanges(string ranges)
{
return new StringBuilder()
.AppendLine(CellsBeginTag)
.AppendLine(cells)
.AppendLine(ranges)
.AppendLine(CellsEndTag)
.ToString();
}

internal static string AddInstructions(string instructions)
internal static string FormatInstructions(string instructions)
{
return new StringBuilder()
.AppendLine(InstructionsBeginTag)
Expand All @@ -136,9 +149,9 @@ internal static string AddInstructions(string instructions)
}

// Render range as Markdown table because models have seen loads of those
internal static string ParseCells(Cells cells)
internal static string RenderRange(Range range)
{
var values = cells.Values switch
var values = range.Values switch
{
ExcelError excelError => throw new ExcelErrorException(excelError),
object[,] manyCells => manyCells,
Expand All @@ -161,14 +174,14 @@ internal static string ParseCells(Cells cells)
// Add header row
for (var c = 1; c < numberOfRenderedColumns; c++)
{
table[0, c] = GetColumnName(cells.ColumnFirst + c - 1);
table[0, c] = GetColumnName(range.ColumnFirst + c - 1);
maxColumnWidth[c] = table[0, c].Length;
}

// Add enumeration column
for (var r = 0; r < numberOfRows; r++)
{
table[r + 1, 0] = GetRowName(cells.RowFirst + r);
table[r + 1, 0] = GetRowName(range.RowFirst + r);
}

// Parse cells and track empty rows, empty columns, and max column width along the way
Expand Down Expand Up @@ -263,14 +276,24 @@ internal static string ParseCells(Cells cells)
if (rowsWithValues.Count == 0)
{
return $"The user has provided the cell range " +
$"{GetColumnName(cells.ColumnFirst)}{GetRowName(cells.RowFirst)}:" +
$"{GetColumnName(cells.ColumnFirst + values.GetLength(1) - 1)}{GetRowName(cells.RowFirst + values.GetLength(0) - 1)}, " +
$"{GetColumnName(range.ColumnFirst)}{GetRowName(range.RowFirst)}:" +
$"{GetColumnName(range.ColumnFirst + values.GetLength(1) - 1)}{GetRowName(range.RowFirst + values.GetLength(0) - 1)}, " +
$"but all cells are empty.";
}

return tableBuilder.ToString();
}

internal static string RenderRanges(IReadOnlyList<Range> ranges)
{
if (ranges.Count == 0)
{
return $"The user provided no additional context.";
}

return string.Join(Environment.NewLine, ranges.Select(RenderRange));
}

private static double ParseTemperature(string temperatureAsString)
{
if (double.TryParse(temperatureAsString.Replace(',', '.'), NumberStyles.Any, CultureInfo.GetCultureInfo("en-US"), out var temperature))
Expand Down
2 changes: 1 addition & 1 deletion src/Cellm/AddIn/Arguments.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@

namespace Cellm.AddIn;

internal record Arguments(Provider Provider, string Model, object Cells, object Instructions, double Temperature, StructuredOutputShape OutputShape);
internal record Arguments(Provider Provider, string Model, IReadOnlyList<Range> Ranges, object Instructions, double Temperature, StructuredOutputShape OutputShape);
Loading
Loading