Yi Tang Data Science and Emacs

Giving Qwen 3.6 35B Vision

Qwen 3.6 35b has been a fantastic thinking companion for me, anything that I don’t know, I am not comfortable with, or having doubts with, I would check with it. I found Qwen 3.6 + DeerFlow 2.0 is much better than the paid version of Grok, and miles better than Perplexity.

Today, I made it even better by giving it vision. Earlier I uploaded an image of my staircase and asked it to check the conditions when I plan the staircase renovation project.

This blog post highlights the key steps of how i did it.

  1. Firstly, Qwen 3.6 has vision encoder built-in already, but it requires an additional mmproj component to make it work. Honestly I have no idea what does it mean at the moment, I just think of it as the eyes to LLM.

  2. Download the mmproj file from the Unsloth Qwen 3.6 repo1, add the path to –mmproj argument for llama-server command, reboot llama.cpp, that’s it.

    The vision component requires additional 1-2GB of vram, so to make them fit to RTX 3090, I had to quantize the mmproj component from bf16 to q4:

    llama-quantize mmproj-BF16.gguf mmproj-Q4_K_M.gguf Q4_K_M
    llama-server Qwen3.6-35B-A3B-UD-Q4_K_M.gguf \
                 --mmproj mmproj-Q4_K_M.gguf \
                 ...  # rest of the llama-server arguments
    
  3. To test it,
    1. check the mmproj is loaded successful from the llama.cpp log,

      9517 alloc_compute_meta: graph splits = 1, nodes = 823                                                                                                                        
      9518 warmup: flash attention is enabled                                                                                                                                       
      9519 srv    load_model: loaded multimodal model, 'mmproj-BF16.gguf'
      
    2. Ask Qwen 3.6 35B model to describe a small image file, using this snippet

      curl -X POST http://192.168.1.34:8000/v1/chat/completions \
        -H "Content-Type: application/json" \
        -d '{
          "model": "Qwen3.6-35B-A3B",
          "messages": [{
            "role": "user",
            "content": [
              {"type": "image_url", "image_url": {"url": "https://picsum.photos/512/512"}},
              {"type": "text", "text": "Describe this image"}
            ]
          }],
          "max_tokens": 100
        }' | jq
      

      This is the response I got, so it confirms it works. The image will change from time to time, so the response will be different.

      The image is a scenic landscape photograph, likely taken in late autumn or winter. It features a vast mountain range in the background, rolling hills in the mid-ground covered in snow and trees, and a foreground of dry, grassy terrain. The sky is dramatic with a mix of blue and warm sunset/sunrise colors.\n\n**2. Breaking down the image into layers

    3. if 1. success, but 2. failed, query the log file, grep vision or image, e.g. this is what I got when i misspell mmproj in llama-server at one point:

      print_info: PAD token             = 248055 '<|vision_pad|>'
      srv    operator(): got exception: {"error":{"code":500,"message":"image input is not supported - hint: if this is unexpected, you may need to provide the mmproj","type":"server_error"}}
      
  4. The model is equipped for vision tasks, next step is to enable vision on DeerFlow 2.0, all I need is adding the support_vision to true in config, full model spec is listed below to avoid ambiguity

    models:                                                                                
    - name: Qwen3.6-35B                                                                    
      display_name: Qwen 3.6 35B (RTX 3090)                                                
      use: langchain_openai:ChatOpenAI                                                     
      model: Qwen3.6-35B                                                                   
      base_url: http://192.168.1.34:8000/v1                                                
      api_key: dummy_key                                                                   
      supports_thinking: true                                                              
      supports_reasoning_effort: true                                                      
      supports_vision: true                                                                
      timeout: 600    
    

    I have to add increase the timeout to 10 mins because the vision component is a lot slower than text generation, with the default value, DeerFlow will throw errors thinking the LLM is not responding. the vision component can be optimised later to reduce the runtime, but so far so good.

  5. Now test DeerFlow 2.0. Restart the services (make docker-stop && make docker-start), open a new chat, upload a PNG file, and ask to describe, wait for a bit, then boom!

    I can also copy an image, and paste it to deerflow, which is very nice interface.


Qwen 3.6 describes an uploaded image in DeerFlow 2.0

Footnotes

1 https://huggingface.co/unsloth/Qwen3.6-35B-A3B-GGUF

Multiple Working Emacs

I work solely inside of Emacs, so when Emacs is down, I cannot do any work. Emacs itself is very reliable, but there might be some risks of downtime when upgrading Emacs or any of the 3rd party libraries that I use.

The downtime can be minimised by always having multiple Emacs versions and their 3rd party libraries available. This blog post documents how I implement it.

Installation

Firstly, install each Emacs into its separate folder, e.g. on my Debian box, I have ~/bin/emacs30.0.92/ installed 8 months ago and ~/bin/emacs30.2/ installed yesterday. This is easy to achieve by adding the prefix option when building Emacs from source, e.g.

 
./configure --with-tree-sitter  --prefix=$HOME/bin/emacs30.2

Daemon

Then have a separate systemd service for each Emacs version. Taking version 30.2 as an example, its unit file is saved as ~/.config/systemd/user/emacs30.2.service.

In that unit file, the Emacs executable is specified in full path to wherever it is installed

[Unit]
Description=Emacs text editor
Documentation=info:emacs man:emacs(1) https://gnu.org/software/emacs/
After=graphical-session.target


[Service]
Type=simple
ExecStart=%h/bin/emacs30.2/bin/emacs --fg-daemon=work --init-directory=%h/.config/emacs/emacs.d_30.2
ExecStop=%h/bin/emacs30.2/bin/emacsclient -s work --eval "(kill-emacs)"
Environment=SSH_AUTH_SOCK=%t/keyring/ssh
Restart=on-failure

[Install]
WantedBy=graphical-session.target

In the unit file, I also added the initial option init-directory so it has its own .emacs.d directory. It ensures the 3rd party packages will be installed there.

Note if there is an init.el file in that directory, Emacs will use that instead of the ancient ~/.emacs file.

GUI

Finally, to open an Emacs GUI that connects to the Emacs 30.2 daemon, run

 
~/bin/emacs30.2/bin/emacsclient -s work -c .

from the command line.

I sometimes found it is more natural to have a desktop application for GUI, so I have ~/.local/share/applications/emacsclient-30.2.desktop file, and the content is

[Desktop Entry]
Name=Emacs 30.2 (Client)
GenericName=Text Editor
Comment=Edit text
MimeType=text/english;text/plain;text/x-makefile;text/x-c++hdr;text/x-c++src;text/x-chdr;text/x-csrc;text/x-java;text/x-moc;text/x-pascal;text/x-tcl;text/x-tex;application/x-shellscript;text/x-c;text/x-c++;x-scheme-handler/org-protocol;
Exec=~/bin/emacs30.2/bin/emacsclient --create-frame -s work %F
Icon=emacs
Type=Application
Terminal=false
Categories=Development;TextEditor;
StartupNotify=true
StartupWMClass=Emacs
Keywords=emacsclient;
Actions=new-window;new-instance;

[Desktop Action new-window]
Name=New Window
Exec=~/bin/emacs30.2/bin/emacsclient --create-frame -s work %F

[Desktop Action new-instance]
Name=New Instance
Exec=~/bin/emacs30.2/bin/emacsclient --create-frame -s work %F

Not Perfect But Close

There could still be some risks of downtime due to conflicts between Emacs/package versions, or caused by updating the OS/other programs. These cases are rare, so this setup is good enough for me.

Rebate Architrave

Had a chill day walking around the canal path in London, at 6:30 pm, I was keen to continue on the home office project.

The existing wall is not plumb, so when I put the architrave, there’s a gap. This is a typical issue, and a small gap (less than 3mm) can be filled with deco chalk. In my case, the bottom has a 10mm gap, which I have to address.


10mm gap at the bottom between the architrave and the wall

In general, there are two ways: either add a small piece to the door lining to fill the gap, or rebate the architrave to accommodate the wall protrusion. I jumped to the rebate approach as I didn’t have any additional strips of wood for the first approach (happy skip days).

A quick measurement told me the architrave needs a rebate of 45mm wide, and the depth varies: starting from 1300mm height, reaching to 10mm deep at the bottom.

The easiest way to do this in this scenario is to cut 10mm deep across the board, as it is okay to have some voids behind the architrave, and there are still 25mm for the architrave to be fixed on.

My first attempt was using a track saw: first cut was at 45mm line, from the bottom all the way to the 1300mm mark. The next cut is right next to the previous cut to increase the rebate area. Repeating this process many times to get to the whole 45mm area. The groove in the photos below is made from 3-4 passes.

With a blade kerf of size 1.8mm, I figured it requires 25 cuts to get to 45mm. My efficiency-seeking brain took over and said: There must be a better way.

So I pulled out the Dewalt router from the drawer, set the depth, and clamped the architrave down to the table. The immediate problem I faced was that it didn’t cut in a straight line: it went like 45-60 degrees for some reason, so I couldn’t cut a long groove like I had done with a track saw.


Rebate using a track saw and a router

So I turned the router 90 degree and cut small and short chunks instead. It worked well: the grooves I cut using a track saw serve as a stopping line so I won’t cut extra. It was not perfect because there were tons of dust coming out from the router, and it made so much noise.

I put my headphones on and made a few more passes. I started seeing how it can be done for the whole 1300m length. Then I saw my neighbour over the fence, asking what I was doing. Well, it turned out to be 7:30 pm already, so I had to stop and leave it for tomorrow.

In hindsight, the track saw can do a much better job because I realised only 5-10 passes would be enough. The small pieces between grooves can be knocked off rather easily using a chisel. The track saw has better dust collection, and the noise is much lower.

Another completely different approach is to remove the protrusion on the wall using a multi-tool: placing the blade on the door lining so the cuts will be flush with the door lining, and pre-cutting the 45mm line to have a neat finish.

Terminating Ethernet Cable At Height For CCTV Cameras

On the Ground

The standard Cat 6 plug is a pain to work with: I have to untwist the 4 pairs, make them perfectly straight, lay the 8 wires side by side with no gaps, and then insert all of them into the RJ45 plug in one go.

It sounds easy, but since the wires are flexible, it is actually very hard: very often the wires move around and become misaligned or misplaced during the fitting. If that happened or any other part of it went wrong, I would have to pull out the whole lot and restart again.

I had successes before, usually after a couple of attempts, often accompanied by frustration in between. It requires me to activate the fight mode, give it 100% focus while sitting in an “Orz” position1, so there’s quite a lot of energy poured into it.

At Height

However, even if I want to, it becomes physically impossible when it comes to fitting a plug in the air for the CCTV cameras: the ladder is a bit wobbly with uneven ground underneath it, and it is windy and raining due to a summer storm.

Since I wasn’t happy with the normal Cat 6 plug, I was keen to try new products. So when I first saw the IDC Punch Down to RJ45 Plug from Kenable 2, I ordered a few. It turned out to be a smart little move (this time).

This product has a built-in RJ45 plug that is already wired up, so I can skip that difficult part. All I have to do is punch down the wires into the IDC terminal. Punching down itself is very easy; I can do it half-minded with one hand.

Another benefit is that I can split the fitting into multiple steps, and I can take mini breaks for my arms between steps. Once one or two wires are inserted into the IDC terminal, it binds the cable to the plug. The binding is strong, so it hangs in the air and swings a bit with the wind with no issues. Then I take my time for the rest of the wires. If you don’t appreciate how important it is, trust me, your arms become rather fatigued when working with your hands overhead.

The only flaw with this product is that the punch-down slots are too wide for the impact adjustable punch-down tool; I was lucky to have a tiny punch-down tool at hand to use.

Footnotes

1 For people who don’t know what “Orz” stands for, “O” is head, “z” is legs and hips, and “r” is arms.

2 This post is not affiliated with Kenable

Extend Ethernet Cable

I had to extend the main Ethernet cable that connects the main router in the living room to the secondary router in the new office. Technically, the cable size is spot on, but I had to cut back 2-3 times because a combination of my lack of experience and the LAP data module from Screwfix is rubbish.

It seems like an unusual task given that there are only a few products available on the market. I tested two, and I am happy with the results, so I am documenting here for people who might find it useful.

Jelly Crimps

The first product I tested was from my electrician. It took me a while to find out that its name is Jelly Crimps. You can get it from TLC or Amazon.

The little connector has two long sleeves that host two wires. It has a button in the middle; press it very hard, and it will release the gel. I highly recommend using a piler unless you have super strong figures.

The process is simply: insert the wires, press with a piler to release the gel, and repeat 8 times for each wire.


Jelly Crimps in Use

It costs about £0.2 to extend one cable, so it is very cost-effective. I wasn’t sure it would work, but it does, and my electrician vouches for it.

The only problem with this product is that it is not maintenance-free. According to my electrician, I will have to put these connectors into a back box and put a front cover over it, which changes it to a much bigger job.

Inline Coupler from Kenable

So I decided to look for a better solution, and I found this Cat 6 Inline Coupler from Kenable.

It got 2 terminate blocks built-in, one for the incoming cable, and one for the outgoing. There is a diagram of the Type B protocol printed on the product, so I don’t have to look it up on my phone. All I have to do is punch down the 16 wires one by one. With a quality punch down tool it is a lot easier and quicker than I thought.


Inline Coupler In Use

The product itself is solid, much better quality than the LAP data module. I didn’t have to worry about damaging the terminal or face plate when pushing it against the wall while using a punch-down tool.

The size is on a sweet spot, about 24mm depth, just enough to tuck it into the 25mm service void. I am not sure if it is maintenance-free or not, but I am comfortable leaving it in the service void as it has an enclosing cover on it.

Kenable is the only place that sells it at a reasonable price, about £2 each, while the rest of the sellers is asking for £5 so thank you Kenable for making it affordable.


Full 500 Mbps Speed in the Office

If you have any questions or comments, please post them below. If you liked this post, you can share it with your followers or follow me on Twitter!