Remap the Copilot Key in Linux

the Copilot button
the Copilot button

Nowadays, laptops have a Copilot key on their keyboards. Most laptops replace the right Ctrl key with the Copilot key. However, on most ThinkPads, Copilot has a dedicated key (not a replacement for the right Ctrl key).

But that key is useless for Linux users because most Linux distros don't have the Copilot service. Most Linux users replace it with the right Ctrl button. How about if you already have a dedicated right Ctrl button? Can we replace the Copilot button with something similar to a Copilot service? Yes, you can. It keeps your data more secure (your data is yours) and is free (because using your local resources).

Some Windows users even hate the Copilot feature. The Copilot application sends user's prompts to Microsoft Copilot servers. They claimed that the data would be saved and not for training. Uhm, that's okay if you trust it. It doesn't use your local resources because it is processed on the servers.

We are going to run these easy steps:

  1. Installing the NPU driver
  2. Checking the NPU and GPU usage
  3. Installing Ollama
  4. Installing Open WebUI
  5. Installing DeepSeek-R1 and NeuralChat
  6. Applying it to the Copilot key

I ran these steps on an Intel machine. Maybe later I can try it on an AMD machine and share my experience with you.

Installing the NPU driver

Nowadays, laptops have NPU (Neural Processing Unit), a dedicated chip or processor for AI. Starting from Apple Silicon, Intel Core Ultra Gen 1, and AMD Ryzen AI. It can process AI locally without sending requests or prompts to the server. So, laptops can run AI or small models easily locally.

Linux, especially for Intel, the chip I'm using, doesn't come with NPU drivers. So, we need to install them ourselves. You can follow the steps from https://github.com/intel/linux-npu-driver/releases.

Next, we are going to install OpenVINO. We want to see that the NPU driver is installed.

  1. python3 -m venv openvino_env
  2. source openvino_env/bin/activate
  3. python -m pip install --upgrade pip
  4. pip install openvino==2025.0.0
  5. benchmark_app -h

Make sure that this message does exist on yours too, Available target devices: CPU GPU NPU.

Checking the NPU and GPU usage

sudo intel_gpu_top to check the NPU and GPU usage in Linux. Install intel-gpu-tools if you didn't have it.

Installing Ollama

Installing Ollama is dead simple, curl -fsSL https://ollama.com/install.sh | sh.

Now, let's check sudo systemctl status ollama.service. Make sure it is active (running) and enabled.

  1. sudo systemctl start ollama.service
  2. sudo systemctl enable ollama.service

Installing Open WebUI

Open WebUI is a great UI for Ollama. I think it's the only UI you need for Ollama. It is the same UI as ChatGPT's UI.

I think we should install it via Docker because we want to make it always run on our local machine.

docker run -d --network=host -v open-webui:/app/backend/data -e OLLAMA_BASE_URL=http://127.0.0.1:11434 -e PORT=48080 --name open-webui --restart always ghcr.io/open-webui/open-webui:main

We set OLLAMA_BASE_URL to our local Ollama without SSL and the default PORT to whatever port we want. That port will be reserved because the app will be always running.

Keep the script below to make Open WebUI always updated. Remember to run it when the Open WebUI is running.

docker run --rm --volume /var/run/docker.sock:/var/run/docker.sock containrrr/watchtower --run-once open-webui

Installing DeepSeek-R1 and NeuralChat

DeepSeek-R1 is a trend today. The cool thing is the model is open source under the MIT License, https://huggingface.co/deepseek-ai. That's why we can run it on our local machine and maybe deploy it by ourselves. Run ollama pull deepseek-r1:7b to download it. I think 7b or 7 billion parameters is enough for our local machine.

What is the answer to «please create a fibonacci script in Go»? The deepseek-r1:7b consumed 50% (11 cores) of my CPU and 0% of my GPU and NPU.

total duration:       9m34.481696466s
load duration:        11.18195ms
prompt eval count:    10 token(s)
prompt eval duration: 575ms
prompt eval rate:     17.39 tokens/s
eval count:           3456 token(s)
eval duration:        9m33.893s
eval rate:            6.02 tokens/s

Here is the full response of deepseek-r1:7b.

<think>
Okay, I need to create a Fibonacci sequence generator in Go. Let me think about how to approach this.

First, I know the Fibonacci sequence starts with 0 and 1, right? Each subsequent number is the sum of the two preceding ones. So the sequence goes 0, 1, 1, 2, 3, 5, etc.

The user wants a script that can generate up to a specified number of terms. I should prompt the user for how many terms they want. That means I'll need an interactive part where the user can input their desired count.

In Go, handling user input is straightforward using the os and io packages. I'll use fmt.Scanln() to read a line from standard input. But since the input might be a string with non-numeric characters, I should convert it using strconv.Atoi(). If there's an error converting, I'll print an error message.

Once I have the number of terms as an integer (let's call it n), I can start generating the sequence. I'll use slices to store the Fibonacci numbers because they allow easy appending and efficient memory usage, especially for large sequences.

I'll initialize a slice with the first two numbers: 0 and 1. But wait, if the user wants only one term or zero terms, I should handle those cases too. So before generating, I'll check:

- If n is less than 1, maybe just return an empty slice.                                                                                                                              
- If n is 1, return [0].                                                                                                                                                                - Else, proceed as normal.

Then, for each subsequent term beyond the first two, I'll append the sum of the last two elements in the slice. I'll loop from index 2 up to n-1 (since slices are zero-based) and do that.

After generating the sequence, I'll print it out neatly using fmt.Println(). If there were no terms generated (like when n was less than 1), maybe just print an empty message or a message indicating an invalid input.

I should also test this with some examples. Like if the user enters 5, the output should be [0, 1, 1, 2, 3]. If they enter 0 or a negative number, I'll handle that gracefully.

Wait, what about the initial case when n is 1? The slice starts with two elements, but we only need one. So perhaps after initializing, if n is less than the length of the slice, return the first n elements. Or maybe adjust the loop to run up to min(n-1, len(current slice) - 1). Hmm.

Alternatively, I can start with an empty slice and build it up step by step until I have n terms. That might be more efficient, especially for larger n. Let me think: initialize a slice with the first term if n >=1, then add each next term as the sum of the previous two, appending each time.

But initializing with 0 and 1 takes care of the first two terms. So perhaps it's better to start with that and handle cases where n is less than or equal to 2 separately.

Wait, no. Let me outline the steps:

1. Read user input as integer n.
2. If n <= 0: print error and exit.
3. Initialize fibs := make([]int, max(n, 2)) and fill it with 0 and 1 up to min(2, n). Wait, no.

Wait, maybe a better approach is:

Initialize the slice as empty.

If n >= 1:
   add 0
if n >= 2:
   add 1

Then, for each i from 2 to (n-1):
   fibs = append(fibs, fibs[i-1] + fibs[i-2])

This way, it handles all cases:

- If n is 0: return empty slice.
- n=1: [0]
- n=2: [0,1]
- n>2: build up the rest.

Yes, that seems efficient and covers all edge cases without multiple conditional checks after generation.

So in code:

fibs := make([]int, 0)

if n >= 1 {
   fibs = append(fibs, 0)
}
if n >= 2 {
   fibs = append(fibs, 1)
}

for i := 2; i < len(fibs); i++ { // Wait, no. Wait, initial length is either 1 or 2.

Wait, after the first two ifs:

If n=3: initially [0,1], then loop from 2 to less than 2? No, because if n >=2, we have at least 2 elements. So for i starting at 2 up to (n-1), append fibs[i] = sum of previous two.

Wait, no, the loop should run until len(fibs) reaches n terms. Because initially, it's either length 1 or 2. For example:

Case n=3: initial len is 2. So we need to add one more term (i from 2 up to 2-1? Wait, perhaps a better way is to loop until the slice has less than n elements.

Alternatively, since in each step, we can calculate the next term as sum of last two and append it until we have n terms.

So after initializing fibs:

for i := len(fibs); i < n; i++ {
   next = fibs[i-1] + fibs[i-2]
   fibs = append(fibs, next)
}

Wait, that would work because for each step beyond the initial length, we add one element.

Testing with n=3:

Initial fibs is [0, 1], len=2. i starts at 2 (since len < 3). So in loop:

i=2: less than 3? yes.
next = 1 + 0? Wait no, because for the third term, it's 0+1=1.

Yes, correct.

Then append next, so fibs becomes [0,1,1], which is length 3. Then loop ends since i increments to 3, which is not less than n (3).

Wait, initial len(fibs) after the ifs: if n>=2, it's 2; else, for n=1, len is 1.

So in code:

fibs = make([]int, 0)

if n >= 1 {
   fibs = append(fibs, 0)
}

if n >= 2 {
   fibs = append(fibs, 1)
}

for i := len(fibs); i < n; i++ {
   next = fibs[i-1] + fibs[i-2]
   fibs = append(fibs, next)
}

Yes, that should work.

Now, let's code this:

Read n using fmt.Scanln(). Then convert to integer. Handle possible errors? Probably not necessary if the user enters a valid number, but in practice, we could add error handling for 
non-integer inputs.

But for simplicity, perhaps just read and convert as follows:

n, err := strconv.Atoi(input)

if err != nil {
   print("Error reading input")
   exit
}

Then check n >=1. If not, handle accordingly.

Wait, but in the code above, if n is 0 or negative, fibs remains empty because we don't add anything. Then when printing, it would output an empty slice.

So perhaps after initializing, I should immediately return early if necessary.

Putting all together:

Read input as integer n.

If n <=0: print "Invalid number of terms" and exit.

Else:

Initialize the slice as above.

Then loop to fill up to n terms.

Print the slice.

Testing this logic with various inputs:

Test 1: Input 5

Output should be [0,1,1,2,3]

Yes.

Test 2: Input 0 or negative number. Output empty slice? Or error?

Probably better to handle it by printing an error message and exit.

So in code:

n := ... (converted from input)

if n <=0 {
   fmt.Println("Invalid number of terms")
   os.Exit(1)
}

Else proceed as above.

Wait, but looking back at the initial problem statement: the user should be able to enter a positive integer. So handling negative numbers and zero is appropriate by printing an error 
message.

So in code:

n, err := strconv.Atoi(input)

if n <=0 || err != nil {
   fmt.Println("Please enter a positive integer.")
   os.Exit(1)
}

Then proceed to generate the sequence.

Another edge case: n=1. The output should be [0].

Yes

Now, putting it all together in code.

So the script will:

- Read input as string.
- Convert to integer using strconv.Atoi().
- Check for non-positive integers or conversion errors; if so, print error and exit.
- Else, initialize fibs with 0 and 1 (if n >=2) or just 0 (if n=1).
- Then loop from len(fibs) until it reaches n terms, each time appending the sum of last two elements.

Wait, no. Wait, after initializing, if n is greater than len(fibs), we loop to add more elements.

So in code:

n := ... as integer

fibs = make([]int, 0)

if n >=1 {
   fibs = append(fibs, 0)
}

if n >=2 {
   fibs = append(fibs, 1)
}

for i := len(fibs); i < n; i++ {
   nextVal := fibs[i-1] + fibs[i-2]
   fibs = append(fibs, nextVal)
}

Then print the slice.

Yes.

Now, code this in Go:
                                                                                                                                                                                              
So first import required packages: "io", "os", "strconv".

But since it's a script, we can do:

package main

import (
        "bufio"
        "fmt"
        "os"
        "strconv"
)

func main() {
        var input strings.NewReader()
        fmt.Print("Enter the number of terms: ")
        input.ReadString()

        n, err := strconv.Atoi(input)
        if n <=0 || err != nil {
                fmt.Println("Please enter a positive integer.")
                os.Exit(1)
        }

        fibs := make([]int, 0)

        if n >=1 {
                fibs = append(fibs, 0)
        }

        if n >=2 {
                fibs = append(fibs, 1)
        }

        for i := len(fibs); i < n; i++ {
                nextVal := fibs[i-1] + fibs[i-2]
                fibs = append(fibs, nextVal)
        }

        fmt.Println("Fibonacci sequence:", fibs)
}

Wait, but in the loop for generating terms beyond initial 2.

Testing with n=3:

fibs starts as [0,1]. i is 2 (since len is 2 <3). So enter loop: nextVal = 1 +0? Wait no, wait. The indices are i-1 and i-2.

Wait for the third term (i=2 in this case), which is index 2:

fibs[i-1] is fibs[1], which is 1.

fibs[i-2] is fibs[0], which is 0.

So nextVal = 1+0=1. Append to make [0,1,1].

Yes, correct.

Similarly for i=3 (if n was 4), but that's beyond the initial len of 3: in code, when n>= len(fibs) (>2?), then we add more terms.

Wait no: len(fibs) after initialization is max(1, 2). Then loop runs from len to less than n. For example:

If n=4:

len(fibs)=2 initially? No: if n >=2, it appends 1. So for n=3, len=2.

Wait wait no:

When n>=1 and n >=2 (for n>=2), the initial fibs is [0], then becomes [0,1].

So when you have to generate until i  add term
then i becomes 3 -> add another
then i becomes4-> add third

Then len(fibs) would be 5 elements? Because after three appends?

Wait initial len=2. Then loop runs for i from 2 to n-1 (since the condition is i =2.

Then runs while i 

To solve this problem, we need to generate a Fibonacci sequence based on the number of terms specified by the user. The Fibonacci sequence starts with two initial values (0 and 1) and each subsequent term is the sum of the previous two terms.

### Approach
1. **Reading Input**: We read an integer input from the user which specifies the number of terms in the Fibonacci sequence.
2. **Input Validation**: If the input is not a positive integer, we print an error message and exit.
3. **Initialization**: Depending on the value of the input, we initialize our Fibonacci sequence with appropriate starting values:
   - If the input is 1, the sequence starts with [0].
   - If the input is greater than or equal to 2, it starts with [0, 1].
4. **Generating Sequence**: Using a loop, we generate each subsequent term by summing the previous two terms until we reach the desired number of terms.
5. **Output**: Finally, we print the generated Fibonacci sequence.

### Solution Code
```go
package main

import (
        "bufio"
        "fmt"
        "os"
        "strconv"
)

func main() {
        var input strings.NewReader()
        fmt.Print("Enter the number of terms: ")
        input.ReadString()

        n, err := strconv.Atoi(input)
        if n <= 0 || err != nil {
                fmt.Println("Please enter a positive integer.")
                os.Exit(1)
        }

        fibs := make([]int, 0)

        if n >= 1 {
                fibs = append(fibs, 0)
        }
        if n >= 2 {
                fibs = append(fibs, 1)
        }

        for i := len(fibs); i < n; i++ {
                nextVal := fibs[i-1] + fibs[i-2]
                fibs = append(fibs, nextVal)
        }

        fmt.Println("Fibonacci sequence:", fibs)
}
```

### Explanation
1. **Reading Input**: The program starts by reading the user's input using `input.ReadString()`.
2. **Validation Check**: It converts the input string to an integer and checks if it is a positive integer.
3. **Initialization of Fibonacci Sequence**:
   - If the input (n) is 1, the sequence starts with [0].
   - For n ≥ 2, the sequence starts with [0, 1].
4. **Generating Terms**: Using a loop, each term from index 2 to n-1 is generated by summing the two preceding terms.
5. **Output**: The resulting Fibonacci sequence is printed.

This approach efficiently handles all valid inputs and generates the correct sequence of numbers using simple arithmetic operations within a loop.

The deepseek-r1:7b thought a lot and gave a reasonable answer. Let's see the NeuralChat's performance.

total duration:       18.094297389s
load duration:        9.551611ms
prompt eval count:    27 token(s)
prompt eval duration: 1.801s
prompt eval rate:     14.99 tokens/s
eval count:           124 token(s)
eval duration:        16.282s
eval rate:            7.62 tokens/s

Here is the full answer from the NeuralChat.

// Fibonacci sequence calculation using recursion in Go programming language.
package main

import "fmt"

// Function to calculate Fibonacci sequence using recursion.
func fib(n int) int {
        if n <= 1 {
                return n
        }
        return fib(n-1) + fib(n-2)
}

func main() {
        for i := 0; i < 15; i++ {
                fmt.Println(fib(i))
        }
}

NeuralChat with 7 billion parameters is slightly faster than DeepSeek-R1 with 7 billion parameters too. NeuralChat's answer is straight to the point. DeepSeek's answer is reasonable. Both of them use 50% of CPU resources. Did I miss something or is this because the Ollama didn't optimize enough Intel's GPU or NPU? I set OLLAMA_INTEL_GPU=true as my environment variable but I think it has no effect.

Applying it to the Copilot key

We need the Chromium browser, the open-source version of Google Chrome, sudo apt install chromium-browser.

  1. Settings > Keyboard > Keyboard Shortcuts > View and Customize Shortcuts > Custom Shortcuts > Add Shortcut
  2. Name: Open WebUI. Command: chromium-browser --app=http://localhost:48080. Shortcut: Fn + Copilot.

Now, let's hold the Fn key then press the Copilot key. The Chromium browser with Open WebUI is shown up.

I know it's not clean because we need to hold the Fn key. To remap only the Copilot button requires a little effort. Maybe I'll update this article later if I find a more elegant way.

Related Articles